Function call ambiguity in templatized code - c++

Here is a (very distilled) use case and code example (sorry if it doesn't look too minimal, I couldn't figure out what else to exclude. The complete 'just compile' code can be found here: https://gcc.godbolt.org/z/5GMEGKG7T)
I want to have a "special" output stream, which, for certain user types, does special treatment during stream processing.
This special stream should be able to stream a "special" type with "special" treatment, as well as any other type which could be streamed through conventional streams - in the latter case, we simply use the conventional stream directly
struct Type { }; // Special type
struct Stream { // Special stream
// In this distilled example, we could achieve the same result
// by overriding << for ostream and Type, but the actual use case is wider
// and requires usage of the special stream type
// For illustration purposes only, we use this overload to cout "T!" for Types
Stream& stream(Type t) { std::cout << "T!"; return *this;}
// For any type for which cout << T{} is a valid operation, use cout for streaming
template<class T>
auto stream(const T& t) -> decltype(std::cout << std::declval<T>(), std::declval<Stream&>())
{
std::cout << t; return *this;
}
};
// Consumes every T for which Stream.stream is defined - those would be Type
// as well as anything else which can be sent to cout
template<class T>
auto operator<<(Stream& s, const T& t) -> decltype(s.stream(t))
{
s.stream(t);
return s;
}
This code works as expected:
void foo()
{
Stream stream;
stream << 10 << Type{};
}
Now I want to define a different struct, which has a member of Type in it, and a streaming operator for the same struct:
struct Compound {
Type t;
int i;
};
// We want the `<<` operator of the struct templated, because we want it to work with
// any possible stream-like object out there
template<class S>
S& operator<<(S& s, Compound comp)
{
s << comp.t << " " << comp.i;
return s;
};
Unfortunately, this doesn't work well. An attempt to use this << operator:
void bar(Compound comp)
{
Stream s;
s << comp;
}
Triggers an ambiguity:
error: ambiguous overload for 'operator<<' (operand
types are 'Stream' and 'Compound')
note: candidate: 'decltype (s.Stream::stream(t)) operator<<(Stream&, const T&)
note: candidate: 'S& operator<<(S&, Compound) [with S = Stream]'
That makes sense, since after defining templated operator for << for Compound, it could be streamed via cout, as well as through this new operator!
I can certainly work aorund this by making << for Compound non-templated, and instead use the special Stream as a stream argument - but I'd like to avoid it.
Is there a way I can preserve the templated << for Compound and still avoid the ambiguity?

So you need to lower the priority of operator<<(Stream& s, const T& t) to make it the last resort.
You can do it by making it (seemingly) less specialized, by templating the first parameter:
auto operator<<(std::same_as<Stream> auto &s, const auto &t) -> decltype(s.stream(t))
{
s.stream(t);
return s;
}
The change I made to the second parameter is unimportant, it's just for terseness.

Some extra boilerplate maybe?
template<class T> struct IsDefault: std::true_type {};
template<class T> concept Default = IsDefault<T>::value;
struct Stream {
template<Default T> Stream &operator<<(T &&t) {
return std::cout << t, *this;
}
};
struct Compound {};
template<> struct IsDefault<Compound>: std::false_type {};
Stream &operator<<(Stream &s, Compound &&c) { return s << "Compound"; }

Related

Is there a way to get the type of a dereferenced value from a dereference-able type?

I'm playing around with C++ concepts and came across an interesting problem. I have the following two custom-defined concepts:
template<typename T>
concept is_dereferencable = requires (T t) { *t; };
template<typename T>
concept is_printable = requires (T t) { std::cout << t; };
As the names suggest, the first one is used to determine if a given type can be dereferenced, while the other one to check if a type supports output operator. I also have a function template called println, which looks like this:
template<typename T>
void println(const T& t)
{
if constexpr (is_dereferencable<T>) {
if constexpr (is_printable<decltype(*t)>) {
std::cout << *t << '\n';
}
} else if constexpr (is_printable<T>) {
std::cout << t << '\n';
}
}
This prints the dereferenced value *t if and only if type T is dereference-able and the type of the dereferenced value is printable. So, for example, I can use this function template with something like an std::optional:
int main
{
std::optional<std::string> stringOpt {"My Optional String"};
::println(stringOpt);
return 0;
}
This will print My Optional String as expected. While this is nice, the function will just silently print nothing if the type of a dereferenced value of a derefernce-able is not printable. So, for a user-defined type Person, the following will just print nothing:
struct Person
{
std::string m_name;
explicit Person(const std::string& name) : m_name {name} {}
};
int main
{
std::optional<Person> personOpt {"John Doe"}
::println(personOpt);
return 0;
}
So I would like to move the above compile-time ifs to a requires clause itself in order to get compile time errors in such cases. Is there a way to achieve that? Is there a way to get the dereferenced type of a given template type T? To make it a bit clearer, I would like to have something like this:
template<typename T>
requires is_dereferencable<T> && is_printable<decltype(*T)>
void printDereferencable(const T& t)
{
std::cout << *t << '\n';
}
P.S.: I understand that I could remove the nested if and just fail upon trying to call an output operator on something that doesn't support it. However, I want to specifically move this compile-time error to the concept to get a clearer error message.
another option is put the constraint after parameters, so it has access to them.
template<typename T>
void printDereferencable(const T& t)
requires is_dereferencable<const T&> && is_printable<decltype(*t)>
{
std::cout << *t << '\n';
}
note: it should test on const T&, not T
you can use std::declval
template<typename T>
requires is_dereferencable<const T&> && is_printable<decltype(*std::declval<const T&>())>
void printDereferencable(const T& t)
{
std::cout << *t << '\n';
}
or you can just write a is_dereference_printable concept
template<typename T>
concept is_dereference_printable = requires (T t) { std::cout << *t; };

Mixing delete keyword with concepts and constraints

In my question I received an answer which suits me, but I don't understand how does it work.
Especially, I don't understand how delete keyword along with concepts remove overloads for operator<<.
(I will paste piece-by-piece, refactored version of the code from the accepted answer.)
enum class LogLevel
{
info,
warning,
error
};
template<typename T>
concept HasLogMethodReturningReferenceToSelf = requires(T v)
{
{
v.log(LogLevel{})
} -> std::convertible_to<T&>;
};
So, here we define a concept, which checks whether a type has method log(), which takes LogLevel as a parameter and returns convertible to reference to self.
Then we delete operator<< overloads (block implicit function generation and explicit overloads) for overloads which have on the left-side of << a type which satisfies HasLogMethodReturningReferenceToSelf and on the right-side of << a type which is not a std::string:
template<HasLogMethodReturningReferenceToSelf T, class U>
requires(!std::convertible_to<U, std::string>) auto operator<<(T, U) = delete;
I don't understand when the overloads would be deleted? During "instantiation" of the concept for some specific type T? Because, not for each type which satisfies the criteria (it would break the codebase?)?
Because later, another concept is defined, which checks for output stream operator overloads for basic types:
template<typename T>
concept HasOutputStreamOperatorOverloadsForBasicTypes =
requires(T v, unsigned unsigned_, int int_, float float_, unsigned char unsigned_char_, char char_)
{
{
v << "string literal" //
<< unsigned_ //
<< int_ //
<< float_ //
<< unsigned_char_ //
<< char_ //
} -> std::convertible_to<T&>;
};
Finally, we define a Loggable concept:
template<typename T>
concept Loggable = HasLogMethodReturningReferenceToSelf<T> && HasOutputStreamOperatorOverloadsForBasicTypes<T>;
Normally, I would define one big Loggable concept:
template<typename T>
concept Loggable = requires(T v)
{
{
v.log(LogLevel{})
} -> std::convertible_to<T&>;
{
v << "string literal" //
<< unsigned_ //
<< int_ //
<< float_ //
<< unsigned_char_ //
<< char_ //
} -> std::convertible_to<T&>;
};
And then somehow restrict implicit conversion. I guess that the author of the answer had to split those requires expressions to two, because otherwise, when I use the latter Loggable definition and then delete the overloads after the definition:
template<Loggable T, class U>
requires(!std::convertible_to<U, std::string>) auto operator<<(T, U) = delete;
I get compile error:
fatal error: recursive template instantiation exceeded maximum depth of 1024
Why?
I don't understand when the overloads would be deleted? During
"instantiation" of the concept for some specific type T? Because, not
for each type which satisfies the criteria (it would break the
codebase?)?
When we check whether v << unsigned_ is well-formed in the requires clause, if your Logger class does not have the unsigned overload of operator<<, the global operator<< defined before concepts will be selected since U will be directly instantiated as unsigned and there is no implicit conversion, so it has a higher priority.
And because the global operator<< is deleted, so v << unsigned_ is ill-formed and the constraint is not satisfied.

How to overload operator << to act like what ostream does

I am on implementing a class, and I'd like to pass some parameters to the instance using <<.
For example,
terminal term;
term << "Hello World!" << '\n';
The code goes below,
class terminal {
template <typename T>
terminal& operator << (T& t) {
std::cout << t;
return *this;
}
};
Basically, I'd like to be a stream instead of being the part of stream. (not cout << term;)
(Sorry for that I forgot to specify my question)
The question is, it worked well with strings, but it compiled failed if there is a number (like int, char, etc).
If we use the example above, the compiler will complain that
Invalid operands to binary expression ('terminal' and 'int')
I would change to the following, in order for sequencing of operator<< (e.g., term << "hello" << std::endl; ) to work:
namespace foo {
class terminal {
std::ostream &strm;
public:
terminal(std::ostream &strm_) : strm(strm_) {}
terminal() : strm(std::cout) {}
template <typename T>
friend std::ostream& operator<<(terminal &term, T const &t);
};
template <typename T>
std::ostream& operator<<(terminal &term, T const &t) {
term.strm << t;
return term.strm;
}
}
Live Demo
The problem is that your operator << takes its argument by reference to non-const, which means it can only bind to lvalues. So things like non-string literals are out. If you do not need to modify the argument, take it by const & instead. The language has a special rule which allows lvalue references to const to bind to rvalues as well.
terminal& operator << (const T& t) {
std::cout << t;
return *this;
}
If you do need to modify the argument, use a different approach than <<. A stream-like interface which would modify the streamed arguments during << would be extremely counterintuitive and a maintenance nightmare.

enum and static const member variable usage in template trait class

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 { };

How to filter input streamed to clog

I'am currently trying to filter input streamed to the 'clog' streambuffer. Therefore i wanted to create a class 'MyClog' which overloads it's << operator and filters the input streamed to it before forwarding the filtered input to the clog streambuffer.
The filtering depends on an internal state of the MyClog class. Also i want to overload the operator using a template, so i just have to implement one method.
example
clog << "This is a Test No." << 1;
myclog << "This is a Test No." << 2
Depending of the internal state of myclog the message is printed out via clog.
My overloaded operator works if i'am not using a template, what am i doing wrong? And what is the difference between template<typename T> and template<class T>?
Here is an excerpt of my code.
template<typename T>
class MyClog {
private:
bool state=false;
public:
MyClog& operator<<(const T& arg){
if(state){
clog << arg;
}
return *this;
}
template<typename T> is preferred over template<class T>, but they are equivalent.
You are templating the entire class when you only need to template one function - this should work:
class MyClog {
private:
bool state=false;
public:
template<typename T>
MyClog& operator<<(const T& arg){
if(state){
clog << arg;
}
return *this;
}