Reputation: 153
I am having some trouble understanding the conditions under which C++ will use an implicit conversion. Suppose I have a class:
class SomeClass
{
private:
int val;
public:
SomeClass(int i) :val{ i } {}
operator string() { return to_string(val); }
};
Why is it that when using this class with operators I need to cast to string? Why doesn't it just perform the conversion implicitly?
code:
int main(void)
{
SomeClass sc = 4;
cout << (string)sc << endl; //prints 4, compiles fine
cout << sc << endl;//does not compile, no operator "<<" matches these operands
string str1 = sc;//compiles fine, performs cast
string str2 = "Base String" + sc;//does not compile, no operator "+" matches these operands
}
This question is more academic than practical as just using a cast is more readable anyway.
Upvotes: 5
Views: 129
Reputation: 50550
std::cout
doesn't accept a std::string
.
It accepts a templated std::basic_string
, the parameters of which are to be deduced.
See here for further details.
Let's consider the following example:
#include<string>
struct SomeClass {
operator std::string() { return ""; }
};
template <class CharT, class Traits, class Allocator>
void f(const std::basic_string<CharT, Traits, Allocator> &) {}
template <class CharT, class Traits, class Allocator>
void f(std::basic_string<CharT, Traits, Allocator> &) {}
int main(void) {
SomeClass sc{};
f((std::string)sc);
//f(sc);
}
It is similar to what happens in your case.
Of course, f(sc)
doesn't compile for almost the same reason.
The problem is that to deduce the template parameters SomeClass
should be converted to a std::string
. To be able to convert it to a std::string
, the compiler should guess that a valid overload exists and try to use it. Anyway, such a viable function doesn't exist from its point of view, for template parameters are still to be deduced and to deduce them the compiler should guess that an overload for SomeClass
exists after a cast. It can't, because... And so on.
I haven't checked it, but I would bet that something similar applies to operator+
too.
Upvotes: 2
Reputation:
The operator<<
overload that gets used when you write cout << (string)sc
is a function template:
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);
This is because std::string
is itself a class template, and can be instantiated with other types than char
, and should still be printable if the characters are written to a stream of that same character type. In fact, that's exactly what happens with std::wstring
, where CharT
is wchar_t
rather than char
. That same operator<<
works, provided you use a suitable stream.
When you write cout << sc
, that particular overload is considered. That overload is then rejected because the compiler cannot deduce which type to use for CharT
and the others. It would only be able to deduce the type if it already knew that the conversion would be performed, but the conversions are not considered just yet, they would only be checked in a slightly later stage.
If there were a non-template operator<<
overload that took std::string
, then it would work, sc
would be converted implicitly.
Upvotes: 2