I have a gut-feeling VS2012 is wrong on this one, but I'm not certain.
After looking this question, I felt like trying to implement something similar.
My version works fine on Visual Studio 2012, but does not even compile on Ideone.
Here is my main interface:
#include <iostream>
#include <string>
template <class In, class Out>
struct Pipe
{
typedef In in_type ;
typedef Out out_type ;
In in_val ;
Pipe (const in_type &in_val = in_type()) : in_val (in_val)
{
}
virtual auto operator () () const -> out_type
{
return out_type () ;
}
};
template <class In, class Out, class Out2>
auto operator>> (const Pipe <In, Out> &lhs, Pipe <Out, Out2> &rhs) -> Pipe <Out, Out2>&
{
rhs = lhs () ;
return rhs ;
}
template <class In, class Out>
auto operator>> (const Pipe <In, Out> &lhs, Out &rhs) -> Out&
{
rhs = lhs () ;
return rhs ;
}
Here are a few test classes:
struct StringToInt : public Pipe <std::string, int>
{
StringToInt (const std::string &s = "") : Pipe <in_type, out_type> (s)
{
}
auto operator () () const -> out_type
{
return std::stoi (in_val) ;
}
};
struct IntSquare : public Pipe <int, int>
{
IntSquare (int n = 0) : Pipe <in_type, out_type> (n)
{
}
auto operator () () const -> out_type
{
return in_val * in_val ;
}
};
struct DivideBy42F : public Pipe <int, float>
{
DivideBy42F (int n = 0) : Pipe <in_type, out_type> (n)
{
}
auto operator () () const -> out_type
{
return static_cast <float> (in_val) / 42.0f ;
}
};
And here's the driver:
int main ()
{
float out = 0 ;
StringToInt ("42") >> IntSquare () >> DivideBy42F () >> out ;
std::cout << out << "\n" ;
return 0 ;
}
Ideone is complaining about template deductions and its unable to find the correct operator>>
candidate function:
prog.cpp: In function ‘int main()’:
prog.cpp:75:21: error: no match for ‘operator>>’ (operand types are ‘StringToInt’ and ‘IntSquare’)
StringToInt ("42") >> IntSquare () >> DivideBy42F () >> out ;
^
prog.cpp:75:21: note: candidates are:
prog.cpp:23:6: note: Pipe<Out, Out2>& operator>>(const Pipe<In, Out>&, Pipe<Out, Out2>&) [with In = std::basic_string<char>; Out = int; Out2 = int]
auto operator>> (const Pipe <In, Out> &lhs, Pipe <Out, Out2> &rhs) -> Pipe <Out, Out2>&
^
prog.cpp:23:6: note: no known conversion for argument 2 from ‘IntSquare’ to ‘Pipe<int, int>&’
prog.cpp:30:6: note: template<class In, class Out> Out& operator>>(const Pipe<In, Out>&, Out&)
auto operator>> (const Pipe <In, Out> &lhs, Out &rhs) -> Out&
^
prog.cpp:30:6: note: template argument deduction/substitution failed:
prog.cpp:75:35: note: deduced conflicting types for parameter ‘Out’ (‘int’ and ‘IntSquare’)
StringToInt ("42") >> IntSquare () >> DivideBy42F () >> out ;
Which compiler is correct? If Ideone is correct, is there any easy fix to this code?
If your terminal is set to run as administrator only, and you are not launching VS Code as administrator, the terminal will not be able to open. You can either change the default terminal or edit the properties of the terminal exe to not run as administrator.
Build and run your code in Visual Studio To build your project, choose Build Solution from the Build menu. The Output window shows the results of the build process. To run the code, on the menu bar, choose Debug, Start without debugging. A console window opens and then runs your app.
In Solution Explorer, right-click the project and choose Properties. In the side pane, choose Build (or Compile in Visual Basic). In the Configuration list at the top, choose Debug or Release. Select the Advanced button (or the Advanced Compile Options button in Visual Basic).
Ideone (actually, GCC) is correct here. In Visual Studio, it compiles because of an infamous extension which allows temporaries to bind to non-const lvalue references (the standard forbids that).
I see several possible ways to solve this in standard C++:
One, don't use temporaries for the pipeline stages:
int main ()
{
float out = 0 ;
StringToInt stage1("42");
IntSquare stage2;
DivideBy24F stage3;
stage1 >> stage2 >> stage3 >> out ;
std::cout << out << "\n" ;
return 0 ;
}
Two, create a "stay" function (opposite of std::move
), and use that:
template <class T>
T& stay(T &&x) { return x; }
int main ()
{
float out = 0 ;
stay(StringToInt ("42")) >> stay(IntSquare ()) >> stay(DivideBy42F ()) >> out ;
std::cout << out << "\n" ;
return 0 ;
}
Three, provide an overload of operator >>
taking an r-value reference:
template <class In, class Out, class Out2>
auto operator>> (const Pipe <In, Out> &&lhs, Pipe <Out, Out2> &&rhs) -> Pipe <Out, Out2>&
{
return lhs >> rhs; // Notice that lhs and rhs are lvalues!
}
Of course, ideally you'd provide mixed &, &&
and &&, &
overloads as well.
The first template basically fails because you can't bind a prvalue temporary - IntSquare ()
- to a non-const lvalue reference.
no known conversion for argument 2 from ‘IntSquare’ to ‘Pipe<int, int>&’
This says that you can't initialize Pipe<int, int>&
with a prvalue of type IntSquare
. The value category is unfortunately not explicitly mentioned in the error message.
Although this is a standard rule VC++ ignores it to ease (or troublesome) a C++-programmers daily life.
The second template
template <class In, class Out>
auto operator>> (const Pipe <In, Out> &lhs, Out &rhs) -> Out&
{
rhs = lhs () ;
return rhs ;
}
fails because for two different deductions of Out
two different types were deduced - the first being int
(for lhs
) and the second being IntSquare
.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With