I have written code that detects if one object can be streamed into an std::ostream. However, while it works in clang, it fails in gcc. After simplifying the code, the problem seems to occur when two classes have operator<< defined in different namespaces.
Below is the (simplified) code (and here it is on godbolt):
#include <iostream>
namespace test_ns {
// Define two (trivial) classes.
class Class1 { };
class Class2 { };
// First class has ostream operator defined in namespace test_ns
std::ostream & operator<<(std::ostream & out, const Class1 & v) {
return out << "Class1 Output!";
}
}
// Second class as ostream operator defined in global namespace.
std::ostream & operator<<(std::ostream & out, const test_ns::Class2 & v) {
return out << "Class2 Output!";
}
namespace test_ns {
// Simple template that always evaluates to bool (for SFINAE-based reflection)
template <typename EVAL_TYPE> using bool_decoy = bool;
// Two version of HasPrint that test the operator<< into ostream.
// First version preferred if << works...
template <typename T>
bool HasPrint(bool_decoy<decltype( std::declval<std::ostream&>() << std::declval<T>() )>) {
return true;
}
// Second version as a fallback.
template <typename T>
bool HasPrint(...) {
return false;
}
}
int main()
{
std::cout << test_ns::HasPrint<test_ns::Class2>(true) << std::endl;
}
Here is the error I receive in gcc 9.1:
<source>: In instantiation of 'bool test_ns::HasPrint(test_ns::bool_decoy<decltype ((declval<std::basic_ostream<char, std::char_traits<char> >&>() << declval<T>()))>) [with T = test_ns::Class2; test_ns::bool_decoy<decltype ((declval<std::basic_ostream<char, std::char_traits<char> >&>() << declval<T>()))> = <type error>]':
<source>:40:55: required from here
<source>:26:68: error: no match for 'operator<<' (operand types are 'std::basic_ostream<char>' and 'test_ns::Class2')
26 | bool HasPrint(bool_decoy<decltype( std::declval<std::ostream&>() << std::declval<T>() )>) {
| ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~^~~~~~~~~~~~~~~~~~~~
(followed by a long list of candidates)
While I can (and will) refactor the code to circumvent this issue, I don't fully understand what is going wrong here. Am I improperly defining the operators, or is this a problem with gcc?
The simple answer, as hinted by NathanOliver, is that your operator<< in the global namespace isn’t found by ADL, and it can’t be found by unqualified lookup from test_ns::HasPrint because of test_ns::operator<<. The interesting question is why Clang finds it anyway. I can only assume that decltype is somehow confusing it.
Related
Assume that I want to implement operator<< for all types. I would do:
template <typename T>
std::ostream& operator<<(std::ostream& out, T&& t) {
return out << "DEFAULT";
}
But this won't work because of ambiguity (in cases where the already specified operator<< is a free function). So I tried to restrict this with a concept:
template <typename T>
concept printable = requires(const T& t, std::ostream& out) {
out << t;
};
This correctly reports that ints, std::strings and whatnot are printable, but std::vectors or some_user_defined_structs (without overloaded <<) are not.
What I wanted was to use this concept with my (overly generic) operator<<:
#include <iostream>
#include <vector>
template <typename T>
concept printable = requires(const T& t, std::ostream& out) {
out << t;
};
template <typename T>
requires (!printable<T>)
std::ostream& operator<<(std::ostream& out, T&& t) {
return out << "DEFAULT";
}
int main() {
std::cout << std::vector<int>();
}
But this lead to:
In instantiation of 'std::ostream& operator<<(std::ostream&, T&&) [with T = const char (&)[8]]':
recursively required from 'std::ostream& operator<<(std::ostream&, T&&) [with T = const char (&)[8]]'
required from 'std::ostream& operator<<(std::ostream&, T&&) [with T = const char (&)[8]]'
required from here
fatal error: template instantiation depth exceeds maximum of 900 (use '-ftemplate-depth=' to increase the maximum)
6 | out << t;
| ~~~~^~~~
compilation terminated.
It seems that there is an instantiation loop. In order to check whether we should use my <<, printable is being checked and by doing so, << is attempted to be generated, which leads to a loop.
Is there any mechanism that would prevent such loop? Can we constrain types in such a way that the template will be generated only if it needs to? As for the use-cases, assume that, for some reason, I never want the compilation to fail when someone tries to << something to std::cout.
You can't provide an operation if and only if the operation isn't provided. That's inherently self-recursive.
What you can do instead is add another layer of indirection. Like so:
template <typename T>
void print(std::ostream& os, T&& t) {
if constexpr (printable<T>) {
os << t;
} else {
os << "DEFAULT";
}
}
I was messing around with this and was able to come up with something similar to what the question describes, the only difference being that you have to opt in to using it via a using namespace directive. (godbolt demo)
#include <iostream>
#include <vector>
#include <utility>
template <typename T>
// T can be a reference type
concept printable = requires(std::ostream& out, T t) {
out << std::forward<T>(t);
};
template <typename T>
requires (!printable<T>)
std::ostream& default_print(std::ostream& out, T&& t) {
return out << "DEFAULT";
}
namespace default_ostream
{
template<typename T>
std::ostream& operator<<(std::ostream& out, T&& t)
requires requires { default_print(out, std::forward<T>(t)); }
{
return default_print(out, std::forward<T>(t));
}
} // namespace default_ostream
int main()
{
using namespace default_ostream;
std::cout << std::vector{ 0, 1, 2 } << '\n';
std::cout << "Hello!\n";
std::cout << 2.234 << '\n';
}
This program will output the following with all of GCC, Clang and MSVC:
DEFAULT
Hello!
2.234
Putting the default operator<< in a seperate namespace and deferring the !printable<T> requirement to print_generic seems to work. With this you will have to do using namespace default_ostream; if you want this behaviour, which cannot appear at global scope, but is fine at function (or some namespace) scope.
This works because printable doesn't see the generic operator<< when it's used as a requirement to default_print, that way it can't get selected for out << std::forward<T>(t) in the requirement and avoids recursive instantiation.
When you want to use the generic operator<<, you have to bring it into the local scope with using namespace default_ostream;, so it participates in overload resolution and because of its requirement it will only get selected if there's no other operator<< available.
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.
Consider the following example code for overloading the operator<< for a class A:
#include <iostream>
class A {
template <typename T>
friend A &operator<<(A &a, const T &t)
{
std::cout << t << std::endl;
return a;
}
friend A &operator<<(A &a, const std::string &t)
{
return operator<<<std::string>(a, t + "x");
}
};
My intention is that the second operator explicitly calls the first one.
However, in g++ 7.4 this fails with
In function 'A& operator<<(A&, const string&)':
error: 'operator<<' not defined
return operator<<<std::string>(a, t + "x");
^~
error: expected primary-expression before '>' token
return operator<<<std::string>(a, t + "x");
^
I however do not see why this should not compile.
Here is the code in godbolt.
In-class friend functions are not exposed to any scope. Friend injection used to be a thing (before ADL was invented), but now there is no way to call them except with ADL unless you declare them beforehand. In this case, a workaround is to declare the template function outside the class beforehand.
class A;
template <typename T>
A &operator<<(A &a, const T &t);
Call a function instead of calling an operator.
#include <iostream>
class A {
template <typename T>
static A &print(A &a, const T &t)
{
std::cout << t << std::endl;
return a;
}
template <typename T>
friend A &operator<<(A &a, const T &t)
{
return print(a, t);
}
friend A &operator<<(A &a, const std::string &t)
{
return print(a, t + "x");
}
};
You seem to wish to specialize your template function, but you're not doing it quite right. It should look more like this:
template <> friend A& operator<< <std::string>(A &a, const std::string &t)
{
// Print in here some how. It's not exactly clear to me how you intend to
// do this, as doing something like a << t will create infinite recursion
// finally, return a
return a;
}
Your other option is to switch the order of the functions, creating the template function after you create your first function:
friend A &operator<<(A &a, const std::string &t)
{
// Again, still not sure what you want to do here
// I just want to stress again though, don't do something
// like a << t, or operator<<(a,t)
// That will crash hard and fast, as there is no way to resolve
// it. It will create infinite recursion
return a;
}
template <typename T>
friend A &operator<<(A &a, const T &t)
{
std::cout << t << std::endl;
return a;
}
My intention is that the second operator explicitly calls the first one.
So, first, you will need to actually need the first option in that case.
Second, to do this, you will need to choose a type for t. You would do it like this:
operator<< <SomeType>(a,t);
Keep in mind, t will need to be implicitly converted to SomeType. Else, SomeType needs to be created by calling its constructor:
operator<< <SomeType>(a,SomeType(/* parameters to construct a SomeType ... */));
Note: Doing something like operator<< <SomeType>(a,t + "x") will always become infinitely recursive, and ultimately crash. This is because t + "x" is always an std::string. That means the compiler will always call this overload of the function infinitely, until it finally crashes from a stack overflow. So don't do that.
Since std::to_string is added to c++11, I started implementing to_string instead of the more traditional operator<<(ostream&, T). I need to link the two together in order to incorporate libraries that rely on operator<<(ostream&, T). I want to be able to express that if T has operator<<(ostream&, T), use it; otherwise, use std::to_string. I am prototyping a more limited version that enables operator<< for all enum classes.
enum class MyEnum { A, B, C };
// plain version works
//std::ostream& operator<<(std::ostream& out, const MyEnum& t) {
// return (out << to_string(t));
//}
// templated version not working
template<typename T,
typename std::enable_if<std::is_enum<T>::value>::type
>
std::ostream& operator<<(std::ostream& out, const T& t) {
return (out << to_string(t));
}
Compiler says error: no match for 'operator<<' (operand types are 'std::ostream {aka std::basic_ostream<char>}' and 'MyEnum')
cout << v << endl;
Questions:
Why is the template function not found by the compiler?
Is there a way to implement the general solution?
The following will work if there exists a std::to_string that accepts an argument of type const MyEnum (which doesn't exist according to clang-703.0.31).
#include <iostream>
#include <type_traits>
enum class MyEnum { A, B, C };
template<typename T>
typename std::enable_if<std::is_enum<T>::value, std::ostream &>::type
operator<<(std::ostream& out, const T& t) {
return (out << std::to_string(t));
}
int main() {
MyEnum a;
std::cout << a << std::endl;
}
According to the docs, there are two ways of using std::enable_if, one of which is to make the return type of a function only valid if T is an enum type (in your case). This is what this code shows. If std::is_enum<T>::value is true, then std::enable_if<std::is_enum<T>::value, std::ostream &>::type results in std::ostream & and is not defined (which makes the compiler yell at you) otherwise.
You could probably write something like my::to_string which would try to convert to a string user-defined types as well:
namespace my {
template<typename T>
typename std::enable_if<! std::is_void<T>{} && std::is_fundamental<T>{}, std::string>::type
to_string(T t) {
return std::to_string(t);
}
std::string to_string(std::nullptr_t) = delete;
std::string to_string(MyEnum a) {
return "This is an enum";
}
}
Then, you can use my::to_string instead of std::to_string in your operator<<:
return (out << my::to_string(t));
EDIT: using my::to_string now results in a compilation error when it's argument is void or std::nullptr_t.
Why your code didn't work
Look at the example in the docs:
// 2. the second template argument is only valid if T is an integral type:
template < class T,
class = typename std::enable_if<std::is_integral<T>::value>::type>
bool is_even (T i) {return !bool(i%2);}
As you can see, the second template argument is written with class = /* enable_if stuff */, but in your code you do simply template< typename T, /* enable_if stuff */ >. So, if you follow the docs, you'll get the correct result - the compiler will say it couldn't find a specialization of std::to_string that accepted an enum as a parameter.
I want to test whether a class is streamable to ostream& by seeing whether an overload for operator<< is provided. Based on these posts, I tried to write another version using C++11. This is my attempt:
#include <iostream>
#include <type_traits>
namespace TEST{
class NotDefined{};
template<typename T>
NotDefined& operator << (::std::ostream&, const T&);
template <typename T>
struct StreamInsertionExists {
static std::ostream &s;
static T const &t;
enum { value = std::is_same<decltype(s << t), NotDefined>() };
};
}
struct A{
int val;
friend ::std::ostream& operator<<(::std::ostream&, const A&);
};
::std::ostream& operator<<(::std::ostream& os, const A& a)
{
os << a.val;
return os;
}
struct B{};
int main() {
std::cout << TEST::StreamInsertionExists<A>::value << std::endl;
std::cout << TEST::StreamInsertionExists<B>::value << std::endl;
}
But this fails to compile:
test_oper.cpp:40:57: error: reference to overloaded function could not be resolved; did you mean to call it?
std::cout << TEST::StreamInsertionExists<A>::value << std::endl;
/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/../include/c++/v1/ostream:1020:1: note:
possible target for call
endl(basic_ostream<_CharT, _Traits>& __os)
test_oper.cpp:30:17: note: candidate function not viable: no known conversion from 'TEST::NotDefined' to '::std::ostream &'
(aka 'basic_ostream<char> &') for 1st argument
::std::ostream& operator<<(::std::ostream& os, const A& a)
test_oper.cpp:15:15: note: candidate template ignored: couldn't infer template argument 'T'
NotDefined& operator << (::std::ostream&, const T&);
However, if I replace the line
enum { value = std::is_same<decltype(s << t), NotDefined>() };
with
static const bool value = std::is_same<decltype(s << t), NotDefined>();
then everything compiles.
Why is there such a difference between the enum and the bool?
value is an enum of anonymous name in StreamInsertionExists<T>. When you try to do:
std::cout << StreamInsertionExists<T>::value;
The compiler is doing overload lookup on operator<<(std::ostream&, StreamInsertionExists<T>::E). In the typical case, it'd do integral promotion on the enum and stream it as int. However, you additionally defined this operator:
template<typename T>
NotDefined& operator << (std::ostream&, const T&);
That is a better match for the enum than the int version (Exact Match vs integral promotion), so it is preferred. Yes, it's a function template, but non-templates are only preferred if the conversion sequences match - and in this case they don't.
Thus, this line:
std::cout << TEST::StreamInsertionExists<A>::value << std::endl;
which is:
operator<<(operator<<(std::cout, TEST::StreamInsertionExists<A>::value), std::endl);
And the inner most operator<< call will use your NotDefined& template. Thus, the next step would be to find an appropriate function call for:
operator<<(NotDefined&, std::endl);
and there is no such overload for operator<< hence the error.
If you change value to be bool, there is no such problem because there is an operator<< that takes bool exactly: #6. That said, even with bool, your trait is still incorrect as it always returns false. Your NotDefined version actually returns a reference, so you'd have to check against that. And also, NotDefined& means not defined, so you'd have to flip the sign:
static const bool value = !std::is_same<decltype(s << t), NotDefined&>();
However, this is particularly error prone. Now cout << B{}; instead of failing to compile would instead give you a linker error, and cout << B{} << endl; gives you the same confusing overload error involving endl instead of simply statying that you can't stream a B.
You should prefer to just do:
template <typename...>
using void_t = void;
template <typename T, typename = void>
struct stream_insertion_exists : std::false_type { };
template <typename T>
struct stream_insertion_exists<T, void_t<
decltype(std::declval<std::ostream&>() << std::declval<T>())
> > : std::true_type { };