Overload Resolution Failure When Streaming Object Via Implicit Conversion to String

Overload resolution failure when streaming object via implicit conversion to string

14.8.1/4 in C++98

Implicit conversions (clause 4) will be performed on a function argument to convert it to the type of the corresponding function parameter if the parameter type contains no template-parameters that participate in template argument deduction.

Here you would like an instantiation of

template <class charT, class traits, class Allocator>
basic_ostream<charT, traits>&
operator<<(basic_ostream<charT, traits>&,
const basic_string<charT, traits, Allocator>&);

to be deduced without providing explicitly any template arguments. So all of the arguments contain a template-parameter that participates in the template argument deduction and thus none of them can get its value from an implicit conversion.

Implicit conversion to std::string

operator<<(std::basic_ostream&, std::basic_string) is a function template and user defined conversions are not considered during template argument deduction. You need to overload operator<< for your class.

Another option, of course, is a cast

std::cout << static_cast<std::string>(test);

Why does outputting a class with a conversion operator not work for std::string?

That operator is a free template function. User defined conversions do not get checked when matching against a template function arguments, it instead uses type pattern matching (substitution).

In theory a SFINAE overload using std::is_convertable<> would be able to do what you want, but that technique was not used when operator<< that outputs a std::string to a basic_ostream<char> was defined.

A manual overload to output your class to basic_ostream<...> will fix your problem.

I would do this:

struct String {
std::string s;
operator std::string() const {return s;}
friend std::ostream& operator<<( std::ostream& os, String const& self) {
return os<<self.s;
}
};

which has the added benefit of not creating a wasted copy.

Implicit Conversion Operator for Class Objects to Strings

First of all, I think you're not using std::string value as intended. This causes another compile error (at least on gcc 10.2).
It seems like you want a string and you're using a pointer to a string.
This can be fixed by replace string* value with string value, operator string&() with operator string() and *value = "YIKERS' with value = "YIKERS". For the last one you might also want to check initializer lists.

Regarding the current compile error:

The compile error is caused by the code cout << abc trying to use operator<< on an abc which is an object of type A. However, you did not overload this operator.
In your example this could be something like

friend std::ostream &operator<<(std::ostream &output, const A &a ) 
{
output << a.value;
return output;
}

Even if you have a user-defined conversion to std::string you would still get compile time errors. This link explains it better than I think I could Why cannot use cout with user-defined conversion to std::string?

This is how I understand the explanation from link above:

string header defines the following overload for operator<< for std::basic_ostream:

template <class CharT, class Traits, class Allocator>
std::basic_ostream<CharT, Traits>&
operator<<(std::basic_ostream<CharT, Traits>& os,
const std::basic_string<CharT, Traits, Allocator>& str);

Also, std::string is actually a typedef for std::basic_string<char>.

However, if the conversion is implicit the following rule kicks in:

Type deduction does not consider implicit conversions (other than type
adjustments listed above): that's the job for overload resolution,
which happens later.

So, the compiler cannot deduce the second parameter for the function (e.g. const std::basic_string<CharT, Traits, Allocator>& str) from A. It can, however, deduce it for string so you can explicitly convert abc to string like static_cast<string>(abc) and that would work.

Error when using operator with implicitly converted non-fundamental data types

The stream operators for std::complex are template functions. They will not be invoked unless you actual have a std::complex as no conversions happens in template argument deduction. This means the compiler will not find a suitable overload to print a A<std::complex<int>>

You're first case for works because std::basic_ostream::operator << is overloaded to take an int and you are allowed one user defined conversion is overload resolution.


As a work arround you can define your own operator << that takes your wrapper and forward to the underlying types' operator <<. That would look like

template<typename T>
std::ostream& operator <<(std::ostream& os, const A<T>& a)
{
return os << static_cast<T>(a);
}

Forcing C++ to prefer an overload with an implicit conversion over a template

If you want to change the overloads such that the former overload is chosen whenever there is an implicit conversion possible, with the latter being the backup, you can do this with SFINAE via std::enable_if:

#include <type_traits>

void call_function(const std::function<void()>& function)
{
std::cout << "CALL FUNCTION 1" << std::endl;
function();
}

template <typename Function,
// Consider this overload only if...
typename std::enable_if<
// the type cannot be converted to a std::function<void()>
!std::is_convertible<const Function&, std::function<void()>>::value,
int>::type = 0>
void call_function(const Function& function)
{
std::cout << "CALL FUNCTION 2" << std::endl;
function();
}

Demo


Alternatively, if you want to be able to support an unknown number of overloads of call_function with the "CALL FUNCTION 2" being a backup overload in case none of the functions work, you can do this too, but it requires quite a bit more work:

// Rename the functions to `call_function_impl`
void call_function_impl(const std::function<void()>& function)
{
std::cout << "CALL FUNCTION 1" << std::endl;
function();
}

void call_function_impl(const std::function<void(int, int)>& function)
{
std::cout << "CALL FUNCTION 2" << std::endl;
function(1, 2);
}

// The backup function must have a distinct name
template <typename Function>
void call_function_backup_impl(const Function& function)
{
std::cout << "CALL FUNCTION backup" << std::endl;
function();
}


// Implement std::void_t from C++17
template <typename>
struct void_impl {
using type = void;
};

template <typename T>
using void_t = typename void_impl<T>::type;

// Make a type trait to detect if the call_function_impl(...) call works
template <typename Function, typename = void>
struct has_call_function_impl
: std::false_type
{};

template <typename Function>
struct has_call_function_impl<Function,
void_t<decltype(call_function_impl(std::declval<const Function&>()))>>
: std::true_type
{};


// If the call_function_impl(...) call works, use it
template <typename Function,
typename std::enable_if<
has_call_function_impl<Function>::value,
int>::type = 0>
void call_function(const Function& function)
{
call_function_impl(function);
}

// Otherwise, fall back to the backup implementation
template <typename Function,
typename std::enable_if<
!has_call_function_impl<Function>::value,
int>::type = 0>
void call_function(const Function& function)
{
call_function_backup_impl(function);
}

Demo

Why does cout hello choose the non-member version of the operator ?

Why isn't there a member operator<< for the cout object which takes a const char[]?

Character strings are handled separately. Creating member operators for std::basic_string would require all streams to have a dependancy on std::basic_string functionality, which is not always desirable. All of the member operators are for built-in language types, but std::basic_string is a class instead, so it has non-member operator overloads. One could argue that const char[] (which decays to const char*) is a built-in type, but operator<< streams const char* similarly to std::basic_string, and it doesn't make sense to split those implementations in two different areas of the library. Functionality for handling character strings, whether using arrays or classes, are usually grouped together.

Why isn't there a non-member operator<< which takes an integer?

Because there doesn't need to be one. It already exists as a member operator. You are not supposed to call member operators directly (unless you need to, which your example does not). You are supposed to just use the operator normally (stream << ...) and let overload resolution pick the correct member or non-member operator, based on parameters, scoping, etc. If you think about it, a non-member overload would cause an ambiquity error. Should stream << 123 call stream.operator<<(123) or ::operator<<(stream, 123)? There can be only one choice, or the compile fails.

Why does cout << “hello” choose the non-member version of the operator<< ?
Maybe because there is a particular non-member operator<< overload which is a good candidate for a const char[], rather than the member-operator<< overload which takes a void*?

That is exactly why. A typed const char[] parameter is a better match for a narrow string literal then an untyped void* pointer. So, if both operators are in scope (and the const char[] overload is only in scope if you use #include <string>), then the compiler will choose the better matching overload.



Related Topics



Leave a reply



Submit