This question already has answers here:
std::endl is of unknown type when overloading operator<<
(6 answers)
Closed 3 years ago.
This will compile and run if you uncomment the first operator definition:
#include <iostream>
struct logger
{
std::ostream &loggingStream;
logger(std::ostream &ls) : loggingStream(ls) {}
};
/*
logger &operator<<(logger &l, std::ostream & (*manip)(std::ostream &)) {
manip(l.loggingStream);
return l;
}
*/
template<typename T>
logger &operator<<(logger &l, const T &t) {
l.loggingStream << t;
return l;
}
int main() {
logger l(std::cout);
l << "Hello" << std::endl;
return 0;
}
With the comment in place:
error: no match for ‘operator<<’ (operand types are ‘logger’ and ‘<unresolved overloaded function type>’)
Why do I need to provide a non-template overload to handle endl?
std::endl is itself a template. When you have the first overload, its arguments can be deduced by matching to the function pointer. For that is one instance where TAD happens.
With just the operator<< template, what is there to deduce from? Both templates need their arguments deduced.
Because, as a function template, std::endl is an overload set with regard to template argument deduction; and template argument deduction can't work on overload sets (unless it only contains one function of course).
To illustrate, consider:
template<class Function>
void functor(Function f)
{ f(0); }
void g(float) {}
void g(double) {}
functor(g);
There is no reason to favor one version of g over the other, and unless you explicitly specialize functor (functor<void(float)>(f) is fine), template argument deduction must fail.
This is also true if g is a template: http://coliru.stacked-crooked.com/a/8e27a45bbeedd979
Related
I have a namespace with several structs and enum classes inside of it. For each type, I have a toString() method. Here is a small example:
namespace test {
struct A {
int i;
};
struct B {
float j;
};
std::string toString(const A &a){
return to_string(a.i);
}
std::string toString(const B &b){
return to_string(b.j);
}
}
I want to provide a templated operator<< which captures only these types, but not for types outside of this namespace:
template<class T>
std::ostream & operator<<(std::ostream &out, const T &t){
out << toString(t);
return out;
}
However, this gives me the following compilation error:
error: ambiguous overload for 'operator<<' (operand types are 'std::stringstream {aka std::__cxx11:basic_stringstream<char>}' and 'const char*')
How can I write a templated operator overload for this?
I solved it using concept & requires of C++20 (gcc >= 10.1):
template <typename T>
concept HaveToString = requires (T t) {
{ toString(t) };
};
template<HaveToString T>
std::ostream & operator<<(std::ostream &out, const T& t){
out << toString(t);
return out;
}
int main() {
test::A a;
std::cout << a << std::endl;
return EXIT_SUCCESS;
}
EDIT
For C++11:
template<typename T, typename = decltype(toString(std::declval<T>()))>
std::ostream & operator<<(std::ostream &out, const T& t){
out << toString(t);
return out;
}
Or as #MooingDuck mentioned in the comments:
template<typename T>
auto operator<<(std::ostream &out, const T& t) -> decltype(out<<toString(t)) {
out << toString(t);
return out;
}
Explanations
First of all, a really good article about unevaluated operands. It will help to understand what is going on in the expressions: decltype(toString(std::declval<T>())) and decltype(out<<toString(t)) which are both basically doing the same thing-> Setting a rule that any call to this function, have to support the call to toString function with the T parameter type.
First Approach
decltype(toString(std::declval<T>()))
Let's split this complex expression into sub expressions, from the inside out:
decltype(toString( std::declval<T>() ))
std::declval<T>() In some very simple words - means that we are "assuming" we created a variable of the type T at a compile time (If you didn't read the article yet, now it's a really good time to do so). The important thing to know before continue- we didn't do it, the important word is assuming.
decltype( toString(std::declval<T>()) )
The magic continue all the way up to decltype which checking for the type of the unevaluated expression within it. So, if toString that calls T type variable exists, it will return the value that toString function returns. If this function doesn't exist, a compile time error will be thrown (or in this context, the compiler won't deduce this function for the given type).
typename = decltype(toString(std::declval<T>()))
This section in the template meant to enable this function whenever the type returning from decltype is legal.
#MooingDuck Approach
auto operator<<(std::ostream &out, const T& t) -> decltype(out<<toString(t)) { /*...*/ }
Return value: auto
C++11: Deduced by the expression the after the operator ->.
After C++14: Calculated at compile time by the return expression inside the function (if there is no return expression, the return value deduced at compile time to void).
-> decltype(out<<toString(t))
Define the return value type.
decltype(out<<toString(t))
As explained before, whatever comes inside decltype is unevaluated expression. The compiler won't evaluate this expression, but it will make sure that the expression can be evaluated at runtime (or else an exception will be thrown, or in this case, the compiler won't deduce this function), and it will return the type of the returned value from this expression.
I would like to store a std::function in a class as a member.
I have troubles with below test code when calling a.callMethod() where the method has been set just before with a.setMethod(). The code works fine if I remove the template.
I have tried to debug with a function callMethodOutsideClass without success.
Is there a better way to manage that ?
#include <iostream>
#include <vector>
#include <functional>
template<typename T>
struct A
{
A(size_t size, T value) : vec_(size, value), method_(nullptr) {}
void setMethod(const std::function<int(A<T>&)> method) { method_ = method; }
int callMethod()
{
if(method_)
return method_(*this);
else
std::cerr << "method not initialized" << std::endl;
return -1;
}
std::vector<int> vec_;
std::function<int(A<T>& a)> method_;
};
template<typename T>
int callMethodOutsideClass(struct A<T>& a, const std::function<int(A<T>&)> method)
{
return method(a);
}
template<typename T>
int apple(struct A<T>& a)
{
a.vec_[0] += 1;
return 1;
}
template<typename T>
int orange(struct A<T>& a)
{
a.vec_[0] += 2;
return 2;
}
int main()
{
A<int> a(10,4), b(10,4);
std::cout << callMethodOutsideClass(a, &apple) << std::endl;
a.setMethod(&orange);
std::cout << a.callMethod() << std::endl;
std::cout << a.vec_[0] << std::endl;
}
I currently get the following errors :
Foo6.cpp: In function ‘int main()’:
Foo6.cpp:46:47: error: cannot resolve overloaded function ‘apple’ based on conversion to type ‘std::function<int(A<int>&)>’
std::cout << callMethodOutsideClass(a, &apple) << std::endl;
^
Foo6.cpp:48:21: error: no matching function for call to ‘A<int>::setMethod(<unresolved overloaded function type>)’
a.setMethod(&orange);
^
Foo6.cpp:48:21: note: candidate is:
Foo6.cpp:9:7: note: void A<T>::setMethod(std::function<int(A<T>&)>) [with T = int]
void setMethod(const std::function<int(A<T>&)> method) { method_ = method; }
^
Foo6.cpp:9:7: note: no known conversion for argument 1 from ‘<unresolved overloaded function type>’ to ‘std::function<int(A<int>&)>’
A pointer to function is not a std::function<T>. The std::function<T> signature can't be deduced based on the function address given as an argument. In addition, the compiler can't resolve a proper function template specialization to get its address when a conversion to std::function<T> is requested, since the constructor of std::function<T> is a function template as well.
You need to be more explicit:
std::cout << callMethodOutsideClass<int>(a, &apple<int>) << std::endl;
// ^^^^^ ^^^^^
a.setMethod(&orange<int>);
// ^^^^^
Is there any way to deduce templates parameters "easily" ?
You can modify the signature of callMethodOutsideClass in one of two ways:
Option #1:
Disable a type deduction on a std::function<int(A<T>&)> parameter:
template <typename T> struct identity { using type = T; };
template<typename T>
int callMethodOutsideClass(A<T>& a, const typename identity<std::function<int(A<T>&)>>::type method)
{
return method(a);
}
But you'll have to pay for the type-erasure applied by a std::function.
Option #2:
Let the compiler deduce the real type of a functor object given as an argument:
template <typename T, typename F>
int callMethodOutsideClass(A<T>& a, F&& method)
{
return std::forward<F>(method)(a);
}
In both cases you can just say:
callMethodOutsideClass(a, &apple<int>);
// ^^^^^
Note: You still have to pass the address of a concrete function template specialization by providing a list of template arguments &apple<int>. If you want to get away with a simple &address syntax, then the function taking it needs to declare an exact type of that argument:
template<typename T>
int callMethodOutsideClass(A<T>& a, int(*method)(A<T>&))
{
return method(a);
}
callMethodOutsideClass(a, &apple);
or you could help the compiler resolve the proper overload at a call site:
callMethodOutsideClass(a, static_cast<int(*)(decltype(a)&)>(&apple));
...or, you can use a lambda expression defined as follows:
template<typename T, typename F>
int callMethodOutsideClass(struct A<T>& a, F&& method)
{
return std::forward<F>(method)(a);
}
// in C++11:
callMethodOutsideClass(a, [](decltype(a)& x){return apple(x);});
// in C++14:
callMethodOutsideClass(a, [](auto&& x){return apple(std::forward<decltype(x)>(x));});
As far as the setMethod member function is concerned, the things are easier, since the compiler knows exactly that it expects const std::function<int(A<T>&)> method where T is known (not deduced). So basically, you just need to help the compiler to get the address of a function template specialzation you need at the call site:
a.setMethod(&orange<int>);
a.setMethod(static_cast<int(*)(decltype(a)&)>(&orange));
My class N takes a type T and a varying amount of types F...
What's really happening is that I am overloading operator() to take a function reference and the arguments that that function will be supplied. It's like a bind function. But when the function reference returns void, I don't want to be able to std::cout the value of that function call from operator(). So I added an overload of operator<< for std::cout to NOT do anything when the return-type of the function reference is void but I guess I'm not writing out the function signature correctly because I get errors.
#include <iostream>
#include <utility>
template <typename T>
struct N;
template <typename T, typename ... F>
struct N<T(F...)> {
T operator()(T (&t)(F...), F &&... f) {
return t(std::forward<F>(f)...);
}
};
template <typename ... T>
void operator<< (std::ostream &, const N<void(T...)> &) {}
// don't do anything when void
void f(int, int) {}
int main() {
N<void(int, int)> bind;
std::cout << bind(f, 5, 4); // errors
}
The errors that I'm getting are very long, so I won't post them; they are typical error messages for printing on a function returning void.
The above code fails because I'm printing out a function that returns void; and that is the function f. My overload of operator<< doesn't seem to be affecting anything. What am I doing wrong here? If you need any more detail just say so. Thanks.
In your code, you are explicitly calling operator() on the object bind. (Btw, don't use the name bind for that object, std::bind is overly famous and it will confuse people)
The code does what you asked it to do. It called the functor. Which returned void.
Nothing in your operator<< overload has any impact at all on that functor being called.
Now that it is called, we see we have a std::cout << void -- which also has absolutely nothing to do with your operator<<.
Replace std::cout << bind(f, 5, 4); // errors with std::cout << bind; and your overloaded operator<< will be invoked.
The order of operations in C++ is not left-to-right. a << b(c) first evaluates tmp=b(c), then evaluates a << tmp.
Now, an example of how you could handle this problem would be to specialize your N type to, when it returns a void, to instead return a pseudo_void. pseudo_void blocks all use, except you have a << overload that just consists of return os; (oh, and operator<<( ostream& os, blah ) should always return os).
This can cause other problems. As an example, you'll want a "get rid of pseudo-void" traits classes and the like. But it gives you the syntax you want.
Why wouldn't the following compile:
void f(int8_t a)
{
}
void f(int16_t a)
{
}
typedef boost::variant<int8_t, int16_t> AttrValue;
int main()
{
AttrValue a;
a = int8_t(1);
f(a);
}
With the compiler error:
error C2665: 'f' : none of the 2 overloads could convert all the argument types
could be 'void f(int8_t)'
or 'void f(int16_t)'
However, this is OK:
std::cout << a; // f(a);
Where is Where is std::ostream &operator<<(std::ostream &, const AttrValue &) defined and why is it defined?
Overload resolution happens at compile time, when your boost::variant instance could contain either type, so the compiler has no way of knowing whether to call void f(int8_t) or void f(int16_t).
std::cout << a works because in either case it's calling the same function, std::ostream &operator<<(std::ostream &, const AttrValue &), which inside dispatches on the runtime type of the instance.
You need to write a visitor to perform the dispatching:
struct f_visitor: public boost::static_visitor<void>
{
template<typename T> void operator()(T t) const { return f(t); }
};
boost::apply_visitor(f_visitor(), a);
Well, for one thing you are assigning a to itself. And since none of the overloads take a boost::variant type then of course the compiler can't find the correct function.
Also, you may have to use boost::get to get value:
f(boost::get<int8_t>(a));
It happens that operator<< is defined for boost::variant, so the compiler does not need to perform any implicit type conversion. For your f() function, on the other hand, it does not know which conversion to select.
int x = fromString("test") :could not deduce template argument for 'ValueType'
int x = fromString<int>("test") : works fine as expected
So why does the compiler struggle here? I see it with all kinds of real template functions, not just this silly example. It must be a feature of the language, but what?
You can't deduce based on the return type. You can, however, implement a workaround with similar syntax, using the overloaded cast operator:
#include <iostream>
#include <sstream>
#include <string>
using namespace std;
class FromString{
private:
string m_data;
public:
FromString(const char*data) : m_data(data) {}
template<typename T>
operator T(){
T t;
stringstream ss(m_data);
ss >> t;
return t;
}
};
template<> FromString::operator bool(){
return (m_data!="false"); //stupid example
}
int main(){
int ans = FromString("42");
bool t = FromString("true");
bool f = FromString("false");
cout << ans << " " << t << " " << f << endl;
return 0;
}
Output:
42 1 0
C++ doesn't do type inference on the return value. I.e., the fact that it is being assigned to an int isn't used in template parameter deduction.
(Removed edit, since someone else presented the overloaded cast solution already.)
Besides the bad choice for an example (probably makes sense to have int x = to<int>("1235") rather than toString), the problem is that the return type does not participate in overload resolution or type inference[1]. The reason for this is that the expression can be used in many places where the type of the return cannot be deduced:
// assuming template <typename T> T to( std::string ):
//
f( to("123") ); // where there are two overloads f(int), f(double)
int x = 1.5 * to("123"); // T == int? T == double?
to("123"); // now what? returned object can be ignored!
So the decision is that the return type will not take part in overload resolution or type deduction.
[1] There is a single exception to this rule, which is the evaluation of a function pointer with more than one overload, where the overload must be selected by either the destination pointer or an explicit cast, but this is just the one exception and is not used in any other context:
void f();
void f(int);
void g( void (*)() );
void g( void (*)(int) );
void (*p1)() = &f; // overload selected based on destination type
void (*p2)(int) = &f;
g( (void (*)(int))&f ); // overload selected based on explicit cast
It looks like your template has the return type templated which cannot be automatically deduced which is why you need to add it in here.
The return type of a function is dependent on overload resolution, not the other way around.
There is a trick that works though: operator= usually exists only for equal LHS/RHS argument types, except when an explicit operator= is defined (whether as standalone or as a member does not matter).
Thus, overload resolution will find operator=(int &, int), and see if the return value from your function is convertible to int. If you return a temporary that has an operator int, this is an acceptable resolution (even if the operator int is in the generic form of a template<typename T> operator T).
Thus:
template<typename T, typename U>
U convert_impl(T const &t);
template<typename T>
struct convert_result {
convert_result(T const &t) : t(t) { }
template<typename U> operator U(void) const { return convert_impl<U>(t); }
T const &t;
};
template<typename T>
convert_result<T> convert(T const &t) { return t; }