Why does this assignment operation results in ambiguous function call? - c++

Consider following program it compiles and runs fine:
#include <iostream>
#include <string>
using std::string;
struct BB
{
// generic cast
template<typename T>
operator T() const
{
std::cout<<"Generic cast\n";
return 0;
}
// string cast
operator string() const
{
std::cout<<"string cast\n";
return string("hello");
}
};
int main()
{
BB b;
string s = b; // copy constructor
}
But If I slightly change the main() function's code in like following:
int main()
{
BB b;
string s;
s = b;
}
Compiler give following error message (See live demo here)
[Error] ambiguous overload for 'operator=' (operand types are 'std::string {aka std::basic_string<char>}' and 'BB')
Why this call is ambiguous? What is the reason behind that? It looks like there are so many overloaded operator= like one for char, one for char*, one for const char* etc. That's the above program puts compiler into ambiguity.

Your problem is your template conversion operator.
template<typename T>
operator T() const
{
std::cout << "Generic cast\n";
return 0;
}
Allows BB to be converted to anything. Because of that all of the overloads of std::string::operator= that take a different type can be considered. Since they are all valid there is no way to resolve the ambiguity and you get the error.
If you removed the template conversion then it will compile. The template conversion could also be marked as explicit and then you can use a static_cast to the type you want.

You call operator =, but it would be the same if it were a regular function:
void f(int x);
void f(const string &y);
int main() {
BB b;
f(b);
return 0;
}
Since BB can be cast in either int or string, the compiler has no idea which f function to call.
The only reason why your first example works is because the copy constructor is called there, and it only takes const string& as an argument, so there's no multiple choices.

Related

Question about overloaded operator T() in C++ template class

I have this little piece of code:
template<typename T>
class Test
{
public:
//operator T() const { return toto; }
T toto{ nullptr };
};
void function(int* a) {}
int main(int argc, char** argv)
{
Test<int*> a;
function(a);
return 0;
}
It doesn't compile unless the line operator T() const { return toto; } is un-commented. This magically works, but I am not sure why (if I un-comment the line).
I do understand that if the line is commented, the type of a when passed to function() is incompatible with the expected type int*. So, of course, the compiler complains ... no problem.
I also understand that the operator returns the actual type of the object, therefore in this particular case the compiler is happy.
I don't understand why the operator is called in this particular case.
Is doing function(a) the same thing as doing function(a()), only the () are implicit?
operator T() const { return toto; } is a user defined conversion operator, it is not operator(). It's used to define that your class is convertible to a different type.
operator() would look like this instead:
void operator()() const { ... }
In your case, you are using int* as T. If you substitute it yourself in the operator, you will see that it becomes operator int*() const { return toto; } which means "my class can be converted to an int* and the result of that conversion is evaluated as return toto;".
The function function() only accepts an int* as its argument. When you provide a Test instance, the call is only legal if there is a way to convert from Test to int*, which is why the operator T is required for the code to compile.

Implicit type conversion to array

I'll just get right down to it: Why doesn't line 38 implicitly convert to char (&)[32]?
template <size_t StringSize>
struct StringT
{
private:
char mChars[StringSize];
public:
// Note: CharArray can decay to char*.
typedef char (&CharArray)[StringSize];
CharArray array() { return mChars; }
operator CharArray() { return mChars; }
operator const CharArray() const { return mChars; }
};
#include <iostream>
template<size_t Size>
void f(char (&array)[Size])
{
std::cout << "I am char array with size " << Size << "\n";
}
int main()
{
StringT<32> someText;
// Conversion through method compiles.
f(someText.array());
// Explicit conversion compiles.
f((StringT<32>::CharArray)someText);
// Implicit conversion fails:
// source_file.cpp(38): error C2672: 'f': no matching overloaded function found
// source_file.cpp(38): error C2784: 'void f(char (&)[Size])': could not deduce template argument for 'char (&)[Size]' from 'StringT<32>'
// source_file.cpp(19): note: see declaration of 'f'
f(someText);
}
This is currently just a small experiment, but the implicit conversion is highly neccessary if StringT<> is to serve the intended purpose - replacing most of the char arrays in a codebase I am working in.
Thanks in advance.
I understand this is for a refactoring task, and it's probably not feasible in your scenario, however:
If you want to maintain the interface as shown in your main() function, you will need to create a templated overload of f() that will resolve directly to StringT<Size> before any implicit conversion is applied.
template<size_t Size>
void f(StringT<Size>& v)
{
f(v.array());
}
N.B. I made it non-const because your original code has some weird const handling, and I wanted the sample to compile as a drop-in to your OP.

C++ Overloading conversion operator hack

I am trying to implement a hack where I am trying to overload return. I will let the code talk.
namespace tcn
{
class foo
{
std::string blah;
public:
class proxy
{
int intval;
std::string strval;
public:
proxy(int intv, std::string strv) : intval(intv), strval(strv) {};
operator int() const { return intval; }
operator std::string() const { return strval; }
};
foo();
~foo();
proxy get();
};
foo::foo()
{
// Create stuff //
}
foo::~foo()
{
// Destroy stuff //
}
foo::proxy foo::get()
{
int intval = 911;
std::string strval = "Hello!?";
proxy temp(intval, strval);
return temp;
}
}
int main()
{
tcn::foo bar;
std::string ts = bar.get(); // OK
int ti = bar.get(); // OK
ti = bar.get(); // OK
ts = bar.get(); // Compile error here
return 0;
}
If I try to compile the code it gives an error like following
error: ambiguous overload for 'operator=' (operand types are
'std::string {aka std::basic_string}' and 'tcn::foo::proxy')
ts = bar.get();
I was wondering how to overcome this. I have seen other ways to implement this by using 'hints' ,But am trying to give an easy interface to the user. So I am looking for a simple assignment from the user side. How else could this be implemented?
Thank you in advance and if this looks lame- I apologize. I am no that good in C++.
The call is ambiguous because std::string::operator= has overloads taking std::string and char. Both of these can be called using a tcn::proxy: the first using the std::string conversion operator, and the second using the int conversion operator followed by an integral conversion.
Usually overload resolution would prefer the implicit conversion sequence with no necessary standard conversions over the one requiring an integral conversion, but this choice is only considered if the two conversion sequences go through the same function. Since you have two separate paths going through two different functions, the call is ambiguous.
The solution is to not use integral conversions for things like that; it just leads to weird edge cases, surprising behaviour and hidden bugs.

Why doesn't a conversion function work with std::string?

Consider the following class which contains a conversion function for the std::string type:
class SomeType
{
public:
SomeType(char *value)
{
_str = value;
}
operator std::string()
{
return std::string(_str);
}
private:
char *_str;
};
The following snippet fails to compile with the error: no operator "==" matches these operands
int main(int argc, char* argv[])
{
SomeType a("test");
if (a == std::string("test")) // ERROR on this line
{
int debug = 1;
}
return 0;
}
I realize I could define an operator== method that accepts std::string operand, but why doesn't the conversion function work?
The problem is that std::string is in fact a template and as such I imagine that it's comparison operator is also a template. And in that case the standard as I recall states that no implicit conversion will happen for the required arguments, which means you'd have to cast SomeType to string yourself for it to be called.
As stated in Item 46 of Effective C++:
[...], because implicit type conversions are never considered during template argument deduction. Never. Such conversions are used during function calls, yes, but before you can call a function, you have to know which functions exist. [...]
You can find more info here.
std::string is actually typedef to std::basic_string<char, i_do_not_remember>
There no operator == that get just std::string It's template one
template<...>
bool operator (basic_string<...>& a, basic_string<...>& b) {
//
}
But template types can't be deduced here. You may cast it manually:
if (static_cast<std::string>(a) == std::string("test"))

Operator overloading c++ (<<)

This the below program i have written for some test.
class tgsetmap
{
public:
std::map<std::string,std::string> tgsetlist;
void operator<<(const char *str1,const char *str2)
{
tgsetlist.insert( std::map<std::string,std::string>::value_type(str1,str2));
}
};
int main()
{
tgsetmap obj;
obj<<("tgset10","mystring");
obj.tgsetlist.size();
}
This throws a compilation error:
"test.cc", line 10: Error: Illegal number of arguments for tgsetmap::operator<<(const char, const char*).
"test.cc", line 22: Error: The operation "tgsetmap << const char*" is illegal.
2 Error(s) detected.*
Am i wrong some where?
You can't force operator<< to take two arguments on right-hand side. The following code:
obj<<("tgset10","mystring");
does not work as a function call with two arguments but instead just uses the , operator. But it's probably not what you are interested in.
If you need to pass two arguments to the << operator, you need to wrap them in some other (single) type. For example, you could use the standard std::pair, i.e. std::pair<const char*, const char*>.
But note that the operator<< should also return some reasonable type suitable for << chaining. That would probably be a tgsetmap& in your case. The following version should work fine:
#include <map>
#include <string>
#include <iostream>
class tgsetmap
{
public:
typedef std::map<std::string, std::string> list_type;
typedef list_type::value_type item_type;
list_type tgsetlist;
tgsetmap& operator<<(item_type item)
{
tgsetlist.insert(item);
return *this;
}
};
int main()
{
tgsetmap obj;
obj << tgsetmap::item_type("tgset10","mystring")
<< tgsetmap::item_type("tgset20","anotherstring");
std::cout << obj.tgsetlist.size() << std::endl;
}
Note that I've added typedefs to not have to repeat the type names over and over again. I've also made operator<< return a tgsetmap& so that << could be chained (used like in the modified main() above). And finally, I've reused the std::map<...>::value_type to make it simpler but you could also use any other type of your own.
But I believe that you may prefer using a regular method instead. Something like:
void add(const char *str1, const char *str2)
{
tgsetlist.insert( std::map<std::string, std::string>::value_type(str1, str2));
}
(inside the class declaration), and then:
obj.add("tgset10", "mystring");
The operator<< inside of a class must be overloaded like this:
T T::operator <<(const T& b) const;
If you want to overload it with 2 arguments, you can do it outside of a class:
T operator <<(const T& a, const T& b);
My compiler, for example, gives a more detailed error message for the code you posted:
If you are not sure about an operator overloading syntax, there is a wiki article about it.
Yes. operator << is binary operator. not ternary. not forget about this pointer.
As mentioned, the << is binary operator, so there is no way it can take more than two args(One should be this if you are declaring inside the class or a LHS if you are declaring outside the class). However you can accomplish the same functionality by doing obj<<"tgset10". <<"mystring";. But since << is a binary operator, you have to do some hack for this.
For this, I ve assigned a static variable op_count, where in I will determine if it is the value or the type. And another static variable temp_str to store the previous value across invocations.
class tgsetmap
{
public:
std::map<std::string,std::string> tgsetlist;
static int op_count = 0;
static const char *temp_str;
tgsetmap& operator<<(const char *str)
{
op_count++;
if (op_count%2 != 0) {
temp_str = str;
}
else {
tgsetlist.insert( std::map<std::string,std::string>::value_type(temp_str,str));
}
return this;
}
};
So you can do
int main()
{
tgsetmap obj;
obj<<"tgset10"<<"mystring";
obj.tgsetlist.size();
}
Or simply you can embed the value and type in the same string using some separator,
value:type = separator is :
value_type = separator is _.