A flaw in C++ overload resolution rules? - c++

Consider the following code:
#include <iostream>
namespace ns1
{
struct A
{
};
template <class T>
std::ostream& operator << (std::ostream& os, const T& t)
{
return os << "ns1::print" << std::endl;
}
}
namespace ns2
{
template <class T>
std::ostream& operator << (std::ostream& os, const T& t)
{
return os << "ns2::print" << std::endl;
}
void f (const ns1::A& a)
{
std::cout << a;
}
}
int main()
{
ns1::A a;
ns2::f (a);
return 0;
}
Compilation fails with "ambiguous overload error" as per standard.
But why? Surely "equally good" operator in A's 'home' namespace should take precedence? Is there any logical reason not to do it?

If you want the overload in namespace A to be preferred than you'll have to add something to it to make it actually better. Say, by making it not a template:
namespace ns1
{
std::ostream& operator<<(std::ostream&, const A& );
}
Otherwise, there's really no conceptual reason to see why a function template in one namespace would be preferred to a function template in another namespace if both are exactly equivalent. After all, why would the function template in A's namespace be "better" than the function template in f's namespace? Wouldn't the implementer of f "know better"? Relying solely upon function signature sidesteps this issue.

If you carefully read the compiler-errors, the ambiguity error is not between the operator<< versions in ns1 and ns2, but between the operator<<(os, const char*) instantiation from ns1 and the exact same overload from namespace std. The latter is being dragged in by ADL on std::ostream.
The best approach is to use the recommendation by #Barry, and de-templatize the operator<< in namespace ns1, but also to add all functionality related to ns1::A (such as f(A)) into the same namespace:
#include <iostream>
namespace ns1
{
struct A {};
std::ostream& operator << (std::ostream& os, const A& t)
{
return os << "ns1::print" << std::endl;
}
void f (const A& a)
{
std::cout << a;
}
}
int main()
{
ns1::A a;
f(a); // rely on ADL to find ns1::operator<<(os, A)
}
Live Example
Namespace ns1 then acts as the broader interface of class A through ADL.

Related

Fallback for "std::ostream" and "<<" operator using SFINAE and templates in C++17

I'm using Catch2 with TEST_CASE blocks from within I sometimes declare local temporary struct for convenience. These struct sometimes needs to be displayed, and to do so Catch2 suggests to implement the << operator with std::ostream. Unfortunately, this becomes quite complicated to implement with local-only struct because such operator can't be defined inline nor in the TEST_CASE block.
I thought of a possible solution which would be to define a template for << which would call toString() instead if that method exists:
#include <iostream>
#include <string>
template <typename T>
auto operator<<(std::ostream& out, const T& obj) -> decltype(obj.toString(), void(), out)
{
out << obj.toString();
return out;
}
struct A {
std::string toString() const {
return "A";
}
};
int main() {
std::cout << A() << std::endl;
return 0;
}
I have a few questions:
Is the decltype trick modern C++ or can we achieve the same using <type_traits> instead?
Is there a way to require for the toString() returned value to be a std::string and thus disable the template substitution otherwise?
Is it guaranteed that a class with a concrete implementation of operator<< will be prioritized over the template if it exists?
Also, I find this solution to be quite fragile (I get errors when compiling my overall project although this simple snippet works), and I think it can lead to errors because of its implicit nature. Unrelated classes may define toString() method without expecting it to be used in << template substitution.
I thought it might be cleaner to do this explicitly using a base class and then SFINAE:
#include <iostream>
#include <string>
#include <type_traits>
struct WithToString {};
template <typename T, typename = std::enable_if_t<std::is_base_of_v<WithToString, T>>>
std::ostream& operator<<(std::ostream& out, const T& obj)
{
out << obj.toString();
return out;
}
struct A : public WithToString {
std::string toString() const {
return "A";
}
};
int main() {
std::cout << A() << std::endl;
return 0;
}
The downside of this solution is that I can't define toString() as a virtual method in the base class otherwise it prevents aggregate initialization (which is super-useful for my test cases). Consequently, WithToString is just an empty struct which serves as a "marker" for std::enable_if. It does not bring any useful information by itself, and it requires documentation to be properly understood and used.
What are your thoughts on this second solution? Can this be improved somehow?
I'm targeting C++17 so I can't use <concepts> yet unfortunately. Also I would like to avoid using the <experimental> header (although I know it contains useful stuff for C++17).
You can think of both methods as "operator<< on all types with some property".
The first property is "has a toString()" method (and will work in C++11 even. This is still SFINAE, in this case the substitutions are in the return type). You can make it check that toString() returns a std::string with a different style of SFINAE:
template <typename T, std::enable_if_t<
std::is_same_v<std::decay_t<decltype(std::declval<const T&>().toString())>, std::string>,
int> = 0>
std::ostream& operator<<(std::ostream& out, const T& obj)
{
out << obj.toString();
return out;
}
And a non-template operator<< will always be chosen before this template. A more "specialized" template will also be chosen before this one. The rules for overload resolution are a bit complex, but they can be found here: https://en.cppreference.com/w/cpp/language/overload_resolution#Best_viable_function
The second property is "derives from WithToString". As you guessed, this one is more "explicit", and it is harder to accidentally/unexpectedly use the operator<<.
You can actually define the operator inline, with a friend function:
struct A {
std::string toString() const {
return "A";
}
friend std::ostream& operator<<(std::ostream& os, const A& a) {
return os << a.toString();
}
};
And you could also have this friend declaration in WithToString, making it a self-documenting mixin
template<typename T> // (crtp class)
struct OutputFromToStringMixin {
friend std::ostream& operator<<(std::ostream& os, const T& obj) {
return os << obj.toString();
}
};
struct A : OutputFromToStringMixin<A> {
std::string toString() const {
return "A";
}
};

Can I restrict a template operator only for types that do not have that behaviour already specified?

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.

What's preventing Boost.Format form using my stream operator overload for optional int?

I want to be able to use a std::optional<int> with Boost.Format.
#include <iostream>
#include <optional>
#include <boost/format.hpp>
struct SomeType
{
int x;
};
std::ostream& operator<<(std::ostream& os, const SomeType& t)
{
os << t.x;
return os;
}
std::ostream& operator<<(std::ostream& os, const std::optional<int>& t)
{
os << t.value_or(0);
return os;
}
void test()
{
SomeType t{42};
std::cout << (boost::format("%s") % t); //this is fine
std::optional<int> i = 42;
std::cout << (boost::format("%s") % i); //nope
}
The code above gives me the following compiler error:
opt/compiler-explorer/libs/boost_1_68_0/boost/format/feed_args.hpp:99:12: error: no match for 'operator<<' (operand types are 'std::basic_ostream<char>' and 'const std::optional<int>')
os << x ;
~~~^~~~
There are no compiler errors if I simply pass i directly to std::cout.
boost::format("%s") % i invokes a call to operator<<. Name lookup rule is followed during compiling to find a operator<<.
For boost::format("%s") % t, both struct SomeType and std::ostream& operator<<(std::ostream& os, const SomeType& t) is defined in global namespace, by using ADL, operator<< is found.
For (boost::format("%s") % i), std::optional is defined in namespace std, but corresponding operator<< is defined in global namespace. By using ADL, boost won't be able to find it. And
non-ADL lookup examines function declarations with external linkage that are visible from the template definition context,
so the compiler isn't able to find the operator<< which you defined.
A workaround: wrap std::optional inside your own ReferenceWrapper, then define the inserter for your wrapper in the same namespace where ReferenceWrapper is defined.

Specialising a templated function for a templated (Armadillo) class

I'm trying to put together a very simple logging class which treats certain types, and particularly vectors, specially. I want to have a default behaviour when using the << operator, but modify it in certain cases. The set-up is:
class LoggerStream
{
template <typename ArgType>
LoggerStream & operator<< (const ArgType &arg)
{
// Standard logging logic
return *this;
}
template <typename DataType>
LoggerStream & operator<< (const std::vector<DataType> &arg)
{
// Specialised logging logic
return *this;
}
template <typename DataType>
LoggerStream & operator<< (const arma::Col<DataType> &arg)
{
// Specialised logging logic
return *this;
}
LoggerStream & operator<< (const double &arg)
{
// Specialised logging logic
return *this;
}
// Other stuff...
};
The double case works fine. The problem is that for subclasses of the vector types, the general-purpose template seems to take priority, and the more specific template gets ignored.
Since all three templated cases have just one generic template parameter I guess the the vector cases aren't considered the most specialised, but if it were considered ambiguous I would expect a compiler error. (It compiles just fine.) So how can I indicate a specialisation but still generalise over the type of the vector elements? Thanks in advance.
I guess this is to do with some detail of how the Col class is implemented. I'm using (and aliasing) arma::Col<T>::fixed<N> as well, but writing a specific overload for that doesn't seem to help. Any ideas welcome.
I cannot reproduce it. This code works as it is supposed to:
#include <iostream>
#include <vector>
using namespace std;
class LoggerStream
{
public:
template <typename ArgType>
LoggerStream &operator<< (const ArgType &arg)
{
// Standard logging logic
cout << "In general" << endl;
return *this;
}
template <typename DataType>
LoggerStream &operator<< (const std::vector<DataType> &arg)
{
// Specialised logging logic
cout << "In vector" << endl;
return *this;
}
};
int main()
{
LoggerStream foo;
vector<int> v;
foo << v; // calling the vector specialization
}
I'm answering my own question not because I have a resolution (yet), but to clarify the problem after further investigation. In fact this doesn't seem to be specific to arma::Col; rather, the issue seems to be in the precedence given to the more specific overload when the argument is of a subclass type. A simple example that shows the problem without using Armadillo is
#include <iostream>
#include <vector>
using namespace std;
template <typename DataType>
class MyVector : public std::vector<DataType> {};
class LoggerStream
{
public:
template <typename ArgType>
LoggerStream &operator<< (const ArgType &arg)
{
cout << "In general" << endl;
return *this;
}
template <typename DataType>
LoggerStream &operator<< (const std::vector<DataType> &arg)
{
cout << "In vector" << endl;
return *this;
}
};
int main()
{
LoggerStream foo;
std::vector<float> v;
foo << v; // Prints "In vector", as expected
MyVector<float> w;
foo << w; // Prints "In general" (but only if the general case exists)
}
So, using the subclass MyVector produces a call to the general function, not the specialised one. But, if the first version is commented out then both calls produce "In vector". So for some reason both are deemed suitable, but the general function is preferred.
What I still don't know is how to stop this from happening without explicitly writing out a load of specific overloads for individual subclass cases...

Partial template specialization of overloaded << operator

I am having trouble to specialize an overloaded << operator template:
The general template is defined as follows:
template<typename DocIdType,typename DocType>
std::ostream & operator << (std::ostream & os,
const Document<DocIdType,DocType> & doc)
{
[...]
}
The general template works fine. Now I want to specialize the second template parameter. I've tried:
template<typename DocIdType>
std::ostream & operator << <DocIdType,std::string> (std::ostream & os,
const Document<DocIdType,std::string> & doc)
{
[...]
}
When I try to compile this piece of code I get the following compiler error:
"C2768: Illegal use of explicit template arguments"
Can somebody tell me what I am doing wrong?
I could be wrong, but off the top of my head I'd say function templates can't be partially specialized.
Even if they could, prefer straight overloading.
See also Why Not Specialize Function Templates? (by Herb Sutter)
See it Live on Coliru
#include <iostream>
#include <string>
template<typename DocIdType,typename DocType>
struct Document {};
template<typename DocIdType>
std::ostream & operator << (std::ostream & os, const Document<DocIdType,std::string> & doc) {
return os << "for string";
}
template<typename DocIdType,typename DocType>
std::ostream & operator << (std::ostream & os, const Document<DocIdType,DocType> & doc) {
return os << "for generic";
}
using namespace std;
int main(int argc, char *argv[])
{
std::cout << Document<struct anything, std::string>() << "\n";
std::cout << Document<struct anything, struct anything_else>() << "\n";
}
Prints
for string
for generic