This question already has an answer here:
Why cannot use cout with user-defined conversion to std::string?
(1 answer)
Closed 2 years ago.
I'm learning more about implicit conversion operators, and I've noticed something strange with implicit user-defined conversions for strings. Found below is the code.
#include <iostream>
#include <string>
using std::cout;
using std::endl;
using std::string;
class A {
public:
A() {
*value = "YIKERS";
}
operator string&() {
return *this->value;
}
string* value;
};
int main() {
A abc;
cout << abc << endl;
// compile error: invalid operands to binary expression ('std::__1::ostream' (aka
// 'basic_ostream<char>') and 'A')
return 0;
}
Any ideas as to why I'm getting this compilation error? I'm thinking this could mean that the object isn't being implicitly converted to a string? If so, why not? Is there a way to fix this? The conversion operator works perfectly fine for other data types like int, float, char, etc.
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.
Related
I have a class that has both implicit conversion operator() to intrinsic types and the ability to access by a string index operator[] that is used for a settings store. It compiles and works very well in unit tests on gcc 6.3 & MSVC however the class causes some ambiguity warnings on intellisense and clang which is not acceptable for use.
Super slimmed down version:
https://onlinegdb.com/rJ-q7svG8
#include <memory>
#include <unordered_map>
#include <string>
struct Setting
{
int data; // this in reality is a Variant of intrinsic types + std::string
std::unordered_map<std::string, std::shared_ptr<Setting>> children;
template<typename T>
operator T()
{
return data;
}
template<typename T>
Setting & operator=(T val)
{
data = val;
return *this;
}
Setting & operator[](const std::string key)
{
if(children.count(key))
return *(children[key]);
else
{
children[key] = std::shared_ptr<Setting>(new Setting());
return *(children[key]);
}
}
};
Usage:
Setting data;
data["TestNode"] = 4;
data["TestNode"]["SubValue"] = 55;
int x = data["TestNode"];
int y = data["TestNode"]["SubValue"];
std::cout << x <<std::endl;
std::cout << y;
output:
4
55
Error message is as follows:
more than one operator "[]" matches these operands:
built-in operator "integer[pointer-to-object]" function
"Setting::operator[](std::string key)"
operand types are: Setting [ const char [15] ]
I understand why the error/warning exists as it's from the ability to reverse the indexer on an array with the array itself (which by itself is extremely bizarre syntax but makes logical sense with pointer arithmetic).
char* a = "asdf";
char b = a[5];
char c = 5[a];
b == c
I am not sure how to avoid the error message it's presenting while keeping with what I want to accomplish. (implicit assignment & index by string)
Is that possible?
Note: I cannot use C++ features above 11.
The issue is the user-defined implicit conversion function template.
template<typename T>
operator T()
{
return data;
}
When the compiler considers the expression data["TestNode"], some implicit conversions need to take place. The compiler has two options:
Convert the const char [9] to a const std::string and call Setting &Setting::operator[](const std::string)
Convert the Setting to an int and call const char *operator[](int, const char *)
Both options involve an implicit conversion so the compiler can't decide which one is better. The compiler says that the call is ambiguous.
There a few ways to get around this.
Option 1
Eliminate the implicit conversion from const char [9] to std::string. You can do this by making Setting::operator[] a template that accepts a reference to an array of characters (a reference to a string literal).
template <size_t Size>
Setting &operator[](const char (&key)[Size]);
Option 2
Eliminate the implicit conversion from Setting to int. You can do this by marking the user-defined conversion as explicit.
template <typename T>
explicit operator T() const;
This will require you to update the calling code to use direct initialization instead of copy initialization.
int x{data["TestNode"]};
Option 3
Eliminate the implicit conversion from Setting to int. Another way to do this is by removing the user-defined conversion entirely and using a function.
template <typename T>
T get() const;
Obviously, this will also require you to update the calling code.
int x = data["TestNode"].get<int>();
Some other notes
Some things I noticed about the code is that you didn't mark the user-defined conversion as const. If a member function does not modify the object, you should mark it as const to be able to use that function on a constant object. So put const after the parameter list:
template<typename T>
operator T() const {
return data;
}
Another thing I noticed was this:
std::shared_ptr<Setting>(new Setting())
Here you're mentioning Setting twice and doing two memory allocations when you could be doing one. It is preferable for code cleanliness and performance to do this instead:
std::make_shared<Setting>()
One more thing, I don't know enough about your design to make this decision myself but do you really need to use std::shared_ptr? I don't remember the last time I used std::shared_ptr as std::unique_ptr is much more efficient and seems to be enough in most situations. And really, do you need a pointer at all? Is there any reason for using std::shared_ptr<Setting> or std::unique_ptr<Setting> over Setting? Just something to think about.
I have seen other questions on SO regarding this, but none that explains it in full. What is the right ways for compilers to handle the two situations below? I have tried it with gcc 4.7.1 (with -std=c++0x), VS2010 and VS2012 an get different results on all:
Example 1:
struct BB
{
// generic cast
template<typename T>
operator T() const
{
return 0;
}
// string cast
operator std::string() const
{
return string("hello");
}
};
int main()
{
BB b;
string s = b;
}
Output:
gcc 4.7.1: Ok
VS2010: Ok
VS2012: Fail: "Cannot convert from BB to
string"
Example 2:
struct BB
{
// generic cast
template<typename T>
operator T() const
{
return 0;
}
// string cast
operator std::string() const
{
return string("hello");
}
};
int main()
{
BB b;
string s = (string)b;
Output:
gcc 4.7.1: Fail: call of overloaded string(BB&) is ambigious
VS2010: Ok
VS2012: Fail: "Cannot convert from BB to string"
Your second version with a C-style cast is ambiguous. The problem is that there are two ways that static_cast<std::string> can convert an instance of class BB to a string. The obvious path is by using your non-template std::string cast operator to convert directly to a string. There is a more devious path, and all but VS2010 have found it. This other path is to use your template cast operator to convert a BB to an allocator for a string and then construct an empty string using this allocator.
Template cast operators are potentially dangerous beasts. You have just given the compiler the tools to convert a BB to anything.
Your first version escapes this problem because std::basic_string(const _Alloc& __a) is an explicit constructor. Explicit constructors can be used by explicit conversions but not by implicit ones. It is this constructor plus the conversion to an allocator that creates the ambiguity, and this path cannot be used with an implicit conversion.
As to why VS1012 fails on the implied conversion, it could be a bug in VS2012, or it could be that C++11 creates even more avenues to get from a BB to a std::string. I am not a C++11 expert. I'm not even a novice.
One more item: your code would have failed even more miserably had you used
std::string s;
s = b;
The assignment operator in conjunction with the template conversion operator creates more ways to get from b to s. Should the system convert b to a std::string, to a char, or a char*?
Bottom line: You really should rethink your use of template conversion operators.
The reason is fails under some compilers is because they go to different lengths trying to figure out what you're doing. The culprit is the templated operator call.
As this SO question explains, templated operators have to be called explicitly (b.operator()<std::string> if you wanted to call template<typename T> operator();), but, there is no way to call your templated operator:
b.operator std::string()<std::string>; //invalid
b.operator std::string <std::string>(); //also invalid, string takes no template arguments
// a bunch more weird syntax constructions...
The issue comes from the fact that the name after operator depends on the template argument, so there is no way to specify it. gcc and VS2012 figured out what you were doing and noticed they can fit the conversion to either the template or the explicitly defined one => ambiguous call. VS2010 didn't do so and is calling one of them, you can find which one through debugging.
Template specialization could've helped in a case like this, however, trying to define
// string cast
template<>
operator std::string() const
{
return string("hello");
}
will fail, since the compiler can't tell the difference between that function and a regular one without the template<>. If there were some arguments in the prototype it would've worked, but operator typename does have any...
Long story short - avoid templated conversion operators.
Given the following program:
#include <iostream>
#include <string>
using namespace std;
struct GenericType{
operator string(){
return "Hello World";
}
operator int(){
return 111;
}
operator double(){
return 123.4;
}
};
int main(){
int i = GenericType();
string s = GenericType();
double d = GenericType();
cout << i << s << d << endl;
i = GenericType();
s = GenericType(); //This is the troublesome line
d = GenericType();
cout << i << s << d << endl;
}
It compiles on Visual Studio 11, but not clang or gcc. It is having trouble because it wants to implicitly convert from a GenericType to an int to a char but it also could return a string and so there is an ambiguity (operator=(char) and operator=(string) both match GenericType).
The copy constructor is just fine, however.
My question is: How do I resolve this ambiguity without modifying the contents of main? What do I need to do to modify GenericType to handle this situation?
I believe that gcc and clang are correct.
There are two operator= overloads in play:
string& operator=(string const& str); // (1)
string& operator=(char ch); // (2)
Both of these operator= overloads require a user-defined conversion from your argument of type GenericType. (1) requires the use of the conversion to string. (2) requires the use of the conversion to int, followed by a standard conversion to char.
The important thing is that both overloads require a user-defined conversion. To determine whether one of these conversions is better than the other, we can look to the overload resolution rules, specifically the following rule from C++11 §13.3.3.2/3 (reformatted for clarity):
User-defined conversion sequence U1 is a better conversion sequence than another user-defined conversion sequence U2 if
they contain the same user-defined conversion function or constructor or aggregate initialization and
the second standard conversion sequence of U1 is better than the second standard conversion sequence of U2.
Note that an and joins the two parts of the rule, so both parts must be satisfied. The first part of the rule is not satisfied: the two user-defined conversion sequences use different user-defined conversion functions.
Therefore, neither conversion is better, and the call is ambiguous.
[I don't have a good suggestion on how to fix the problem without changing the definition of main(). Implicit conversions are usually not a good idea; they are sometimes very useful, but more frequently they are likely to cause overload ambiguities or other weird overloading behavior.]
There was a gcc bug report in which this problem was described, and resolved as by design: compiler incorrectly diagnoses ambigous operator overload.
I believe GCC is wrong. In the book "The C++ Programming Language" by Bjarne Stroustrup, there is a whole chapter devoted to operator overloading. In the section 11.4.1, the first paragraph says this:
"An assignment of a value of type V to an object of class X is legal if there is an assignment operator X::operator=(Z) so that V is Z or there is a unique conversion of V to Z. Initialization is treated equivalently."
In your example, GCC accepts "string s = GenericType();" yet rejects "s = GenericType();", so it is obviously not treating the assignment the same way as the initialization. That was my first clue something is amiss in GCC.
GCC reports 3 candidates for conversion of GenericType to string in the assignment, all in basic_string.h. One is the correct conversion, one it reports is not valid, and the third causes the ambiguity. This is the operator overload in basic_string.h that causes the ambiguity:
/**
* #brief Set value to string of length 1.
* #param c Source character.
*
* Assigning to a character makes this string length 1 and
* (*this)[0] == #a c.
*/
basic_string& operator=(_CharT __c) {
this->assign(1, __c);
return *this;
}
This isn't a valid conversion because it accepts an operand that doesn't match the type of object that is passed to it. In no place is as assignment to a char attempted, therefore this conversion shouldn't be a candidate at all much less one that causes ambiguity. GCC appears to be mixing up the template type with the operand type in its member.
EDIT: I wasn't aware that assigning an integer to a string was in fact legal, as an integer can be converted to a char, which can be assigned to a string (although a string can't be initialized to a char!). GenericType defines a conversion to an int, thus making this member a valid candidate. However, I still maintain this is not a valid conversion, the reason being that using this conversion would result in two user defined implicit conversions for the assignment, first from GenericType to int, then from int to string. As stated in the same book 11.4.1, "only one level of user-defined implicit conversion is legal."
My question is: How do I resolve this ambiguity without modifying the contents of main?
Create your own class named string that doesn't have an ambiguous operator= and then don't using the std one.
Obviously this isn't a very good solution, but it works and main doesn't have to change.
I don't think you can get the behavior you want any other way.
This solution works
#include <iostream>
#include <string>
#include <type_traits>
using namespace std;
struct GenericType{
operator string(){
return "Hello World";
}
template <typename T, typename = std::enable_if_t <
std::is_same<T, double>::value ||
std::is_same<T, int>::value>>
operator T(){
return 123.4;
}
};
int main(){
int i = GenericType();
string s = GenericType();
double d = GenericType();
cout << i << s << d << endl;
i = GenericType();
s = GenericType();
d = GenericType();
cout << i << s << d << endl;
}
And a more general solution. I think you don't need to create operators for each arithmetic type, since implicit conversions will do the trick.
// ...
template <typename T, typename = std::enable_if_t
<std::is_arithmetic<T>::value && !std::is_same<T, char>::value>>
operator T() const
{
return std::stod("123.4");
}
//...
I have this very simple wrapper template:
template<class T>
struct wrapper {
inline operator T () {
return v;
}
inline wrapper(T v):v(v) { }
T v;
};
Trying to use it with any non-primitive type with (for example) comparison operator relying on the template having it defined doesn't look promising:
std::string t = "test";
assert(t == t);
typedef wrapper<std::string> string_wrapper;
assert(string_wrapper(t) == string_wrapper(t));
GCC 4.4.5 complains with this error:
error: no match for ‘operator==’ in ‘wrapper<std::basic_string<char> >(std::basic_string<char>(((const std::basic_string<char>&)((const std::basic_string<char>*)(& t))))) == wrapper<std::basic_string<char> >(std::basic_string<char>(((const std::basic_string<char>&)((const std::basic_string<char>*)(& t)))))’
What is interesting is that GCC triple-casts the template then fails to use operator == that was defined for std::string.
I don't think that implicit coercion is impossible, since if I change std::string to int or double, bool or anything primitive, GCC will choose the correct operator.
I do not want to define operator == for the wrapper struct, because that operator is just an example, and I need wrapper to 'feel' just like the real type regarding operators.
Just in cast GCC misunderstands my syntax, if I create a wrapper and try to compare it to itself, GCC complains again (though without triple casting) that it cannot find a matching == operator:
typedef wrapper<std::string> string_wrapper;
string_wrapper tw(t);
assert(tw == tw);
error: no match for ‘operator==’ in ‘tw == tw’
Why can't GCC find and/or use std::string operator == std::string when wrapper provides the cast?
That operator T is called a "conversion operator" or "conversion function" instead of a cast. "Conversions" are implicit; casting is explicit.
The compiler can't find the operator== for std::string because the overload resolution rules don't allow that to happen. More details about what you're really trying to do could help provide a solution.
Apparently this is a GCC 4.5 bug. The code is valid, unlike what Fred Nurk says.
http://gcc.gnu.org/bugzilla/show_bug.cgi?id=45383
The questioner has answered itself with some GCC PR, but which really does not answer his question.
The reason is like #Fred describes. Reducing it:
template<typename T>
struct A {
operator T() { return T(); }
};
int main() {
A<std::string>() == A<std::string>();
}
What can the compiler do? It could call operator std::string() on both operands and then do the comparison. But why should it do that call in the first place? It first needs to find an operator== that has two parameters of type std::string. Let's look at how its operator is defined
template<class charT, class traits, class Allocator>
bool operator==(const basic_string<charT,traits,Allocator>& lhs,
const basic_string<charT,traits,Allocator>& rhs);
There we have it. It first needs to do template argument deduction, and the fact that A does not match const basic_string<> will make it so that this operator== is ignored. You are lucky that operator== is found anyway using ADL so that it does argument deduction in the first place (since std::string is a template argument of your type, it will consider namespace std by ADL and find this operator).
So we have no suitable operator== to call and therefor GCC is alright with rejecting your code for the reasons #Fred gave in a nutshell. In the end, trying to make a class behave like another type is deemed to failure.
The following code is giving me a compilation error. Can anyone please tell me why?
class mytype {
public:
int value;
mytype(int a) {
value = a;
}
friend ostream& operator<<(ostream& stream, const mytype& a) {
stream << a.value;//works
return stream;
}
friend ostringstream& operator<<(ostringstream& stream, const mytype& a) {
stream << (a.value);//compilation error
return stream;
}
};
Error:
error C2027: use of undefined type
'std::basic_ostringstream<_Elem,_Traits,_Alloc>'
Upon fixing that:
error C2666: 'operator <<' : 18 overloads have similar conversions
Final fix:
Declare constructor as explicit. Works on MSVC then.
I wonder why.
error C2027: use of undefined type 'std::basic_ostringstream<_Elem,_Traits,_Alloc>'
You need #include <sstream> to get the [i/o]stringstream classes.
About the other errors
The problem with an overload of the form
ostringstream& operator<<(ostringstream& stream, const mytype& a)
is that it matches an ostringstream exactly. Why is it a bad thing to have a more exact overload? From the standard, §13.3.3/1-2:
a viable function F1 is defined to be a better function than another viable function F2 if for all arguments i, ICSi(F1) is not a worse conversion sequence than ICSi(F2) …
If there is exactly one viable function that is a better function than all other viable functions, then it is the
one selected by overload resolution; otherwise the call is ill-formed
Therefore if operator<<( ostream &, int ) and operator<<( ostringstream &, mytype const & ) are both candidates for stream << value, the former matches int exactly with no conversion but the latter matches ostringstream exactly. Therefore neither can be "not worse" for all arguments, and neither candidate may be chosen.
But the code is valid because of a loophole. Your overload is not a candidate at all, except when you actually use your type in the function call. When you declare/define a friend inside a class block, it does not introduce it to any namespace scope; it merely associates it with the class scope, which allows it to be found if that class type describes one of the arguments being passed.
The standard has this to say about friend declarations, although it's in another section (14.6.5):
Friend declarations do not introduce new names into any scope…
So, MSVC tried to be nice and proactively introduced your friend to its enclosing namespace. Strike against MSVC.
However, when I attempted to add a declaration equivalent to what MSVC did "for free," Comeau and GCC nicely resolved the resulting overloading conflict — strike against them. Or is it? As it turns out, the overloaded call occurs earlier in the file than my recommended declaration. If I move the declaration before class mytype { (which requires forward-declaring mytype), then both properly complain about the ambiguity.
Using an overload before it is declared in namespace scope appears to be well and good according to §3.3-3.4 of the Standard. So actually GCC and Comeau were both in the right. The point of declaration is right after the name of the declared object. (And last I checked, self-referential function declarations can still crash GCC.) ADL invokes unqualified lookup into the enclosing namespace at a point immediately before the enclosing class. (3.4.1/8 final bullet, 3.4.1/9, 3.4.2/2a.) If the friend hasn't been declared before the class, it's legitimately not a candidate. (7.3.1.2/3) Isn't C++ a beautiful language?
How keep the simplified example on GCC, but break subsequent code.
friend ostringstream& operator<<(ostringstream& stream, const mytype& a) {
stream << (a.value);//compilation error
return stream;
}
};
ostringstream& operator<<(ostringstream& stream, const mytype& a); // <- here
Following this declaration, it will be impossible to write an int into an ostringstream.
How to break everything uniformly, with simpler declaration semantics.
class mytype; // <- here
// and here:
inline ostringstream& operator<<(ostringstream& stream, const mytype& a);
class mytype {
public:
Following this declaration, it will be impossible to write an int into an ostringstream… including the friend declarations inside class mytype {}.
Actual solution.
The stream classes are supposed to be indistinguishable. If you really want to determine whether a given stream feeds a string in memory (and you shouldn't), it's best to look at its internal streambuf object, returned by rdbuf(), which actually performs the I/O gruntwork. Even a generic ostream object can have ostringstream functionality if given a stringbuf.
if ( typeid( stream.rdbuf() ) == typeid( stringbuf * ) ) {
// this is effectively a stringstream
} else {
// not a stringstream
}
There is an overload ambiguity in the call to operator<< in the ostringstream overload.
stream << a.value;
There are a number of operator<< overloads that are members of the ostream class, which is a base class of ostringstream. One of these is declared as:
ostream& ostream::operator<<(int);
This overload is an exact match for the right-hand side (a.value is an int) but requires a derived-to-base conversion on the left-hand side (stream is an ostringstream, which is derived from ostream).
However, there is also your ostringstream overload:
ostringstream& operator<<(ostringstream&, const mytype&);
This overload is an exact match for the left-hand side (stream is an ostringstream), and a user-defined converting constructor (your mytype(int) constructor) can be used to convert a.value (an int) to a mytype.
Since one overload matches the first argument better and the other overload matches the second argument better, there is an ambiguity. You can fix this either by:
Explicitly converting the left-hand side to an ostream (using (ostream&)stream = a.value;), or
Remove the user-defined conversion by making the constructor explicit.