I have the following interface
virtual void send_command( const std::string& command, const std::string& key, const std::string& value, bool pipeline = true );
virtual void send_command( const std::string& command, const std::string& key, bool pipeline = true );
virtual void send_command( const std::string& command, bool pipeline = true );
that is fully implemented in one class which I will then call as the following:
c.send_command("MUTLI");
c.send_command("SET", "foo", "bar");
c.send_command("GET", "foo");
c.send_command("EXEC");
When I check which method implementation get's called, I see that the third call (GET foo) ends up to hit the last implementation:
virtual void send_command( const std::string& command, bool pipeline = true );
where "foo" is implicit converted to bool. The same counts for "SET", "foo", "bar" which hits the second implementation (string, string, bool)
When I make the call with an explicit c-cast to a string like this
c.send_command("GET", (std::string)"foo");
The expected method gets called.
I am using gcc (GCC) 4.7.2 20121109 (Red Hat 4.7.2-8) with C++11.
You're hitting the fact that pointers can implicitly be converted to bool (testing that they're not null), and that a string literal is not a std::string, but a const char[] which decays into a const char* on call. What you can do is provide an additional overload taking a const char*:
void send_command(const std::string& command, const char* key)
{ send_command(command, std::string(key)); }
Live example
The compiler prefers the conversion from const char* to bool over a conversion to a UDT (std::string). If needed, just add more overloads for const char* as well.
Overload resolution has several steps: First, a set of viable functions is selected. These viable functions must have enough parameters for the call, considering ellipsis and/or default arguments. In addition, each argument has to be convertible to the corresponding parameter type. For c.send_command("GET", "foo"); you have as viable candidates
virtual void send_command( const std::string&, const std::string&, bool);
virtual void send_command( const std::string&, bool);
because the third parameter of the former is defaulted and the string literal arguments are convertible to string const& and bool.
After having the viable set, the compiler looks at the argument conversions needed. No conversion is prefered, then builtin conversions, then user defined conversions. In this case, the conversion for the first argument is the same in both viable functions. For the second argument, the conversion to bool is buitlin, while the conversion to string const& is not. Therefore send_command( const std::string&, bool); is prefered over the alternative.
Related
I fixed a bug recently.
In the following code, one of the overloaded function was const and the other one was not. The issue will be fixed by making both functions const.
My question is why compiler only complained about it when the parameter was 0.
#include <iostream>
#include <string>
class CppSyntaxA
{
public:
void f(int i = 0) const { i++; }
void f(const std::string&){}
};
int main()
{
CppSyntaxA a;
a.f(1); // OK
//a.f(0); //error C2666: 'CppSyntaxA::f': 2 overloads have similar conversions
return 0;
}
0 is special in C++. A null pointer has the value of 0 so C++ will allow the conversion of 0 to a pointer type. That means when you call
a.f(0);
You could be calling void f(int i = 0) const with an int with the value of 0, or you could call void f(const std::string&) with a char* initialized to null.
Normally the int version would be better since it is an exact match but in this case the int version is const, so it requires "converting" a to a const CppSyntaxA, where the std::string version does not require such a conversion but does require a conversion to char* and then to std::string. This is considered enough of a change in both cases to be considered an equal conversion and thus ambiguous. Making both functions const or non const will fix the issue and the int overload will be chosen since it is better.
My question is why compiler only complained about it when the parameter was 0.
Because 0 is not only an integer literal, but it is also a null pointer literal. 1 is not a null pointer literal, so there is no ambiguity.
The ambiguity arises from the implicit converting constructor of std::string that accepts a pointer to a character as an argument.
Now, the identity conversion from int to int would otherwise be preferred to the conversion from pointer to string, but there is another argument that involves a conversion: The implicit object argument. In one case, the conversion is from CppSyntaxA& to CppSyntaxA& while in other case it is CppSyntaxA& to const CppSyntaxA&.
So, one overload is preferred because of one argument, and the other overload is preferred because of another argument and thus there is no unambiguously preferred overload.
The issue will be fixed by making both functions const.
If both overloads are const qualified, then the implicit object argument conversion sequence is identical, and thus one of the overloads is unambiguously preferred.
I've been developing a kind of adapter class, when I encountered a problem under clang. When both conversion operators for lvalue-reference and rvalue reference are defined you get an ambiguity compilation error trying to move from your class (when such code should be fine, as
operator const T& () const&
is allowed only for lvalues AFAIK). I've reproduced error with simple example:
#include <string>
class StringDecorator
{
public:
StringDecorator()
: m_string( "String data here" )
{}
operator const std::string& () const& // lvalue only
{
return m_string;
}
operator std::string&& () && // rvalue only
{
return std::move( m_string );
}
private:
std::string m_string;
};
void func( const std::string& ) {}
void func( std::string&& ) {}
int main(int argc, char** argv)
{
StringDecorator my_string;
func( my_string ); // fine, operator std::string&& not allowed
func( std::move( my_string ) ); // error "ambiguous function call"
}
Compiles fine on gcc 4.9+, fails on any clang version.
So the question: is there any workaround? Is my understanding of const& function modifier right?
P.S.: To clarify - the question is about fixing StringDecorator class itself (or finding the workaround for such class as if were a library code). Please refrain providing answers that call operator T&&() directly or specify conversion type explicitly.
The problem comes from the selection of the best viable function. In the case of the second func call, it implies the comparison of 2 user-defined conversion sequence. Unfortunately, 2 user-defined conversion sequence are undistinguishable if they do not use the same user-defined conversion function or constructor C++ standard [over.ics.rank/3]:
Two implicit conversion sequences of the same form are indistinguishable conversion sequences unless one of
the following rules applies:
[...]
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 [...]
Because a rvalue can always bind to a const lvalue reference, you will in any case fall on this ambiguous call if a function is overloaded for const std::string& and std::string&&.
As you mentioned it, my first answer consisting in redeclaring all functions is not a solution as you are implementing a library. Indeed it is not possible to define proxy-functions for all functions taking a string as argument!!
So that let you with a trade off between 2 imperfect solutions:
You remove operator std::string&&() &&, and you will loose some optimization, or;
You publicly inherit from std::string, and remove the 2 conversion functions, in which case you expose your library to misuses:
#include <string>
class StringDecorator
: public std::string
{
public:
StringDecorator()
: std::string("String data here" )
{}
};
void func( const std::string& ) {}
void func( std::string&& ) {}
int main(int argc, char** argv)
{
StringDecorator my_string;
func( my_string ); // fine, operator std::string&& not allowed
func( std::move( my_string )); // No more bug:
//ranking of standard conversion sequence is fine-grained.
}
An other solution is not to use Clang because it is a bug of Clang.
But if you have to use Clang, the Tony Frolov answer is the solution.
Oliv answer is correct, as standard seems to be quite clear in this case. The solution I've chosen at a time was to leave only one conversion operator:
operator const std::string& () const&
The problem exists because both conversion operators are considered viable. So this can be avoided by changing type of implicit argument of lvalue conversion operator from const& to &:
operator const std::string& () & // lvalue only (rvalue can't bind to non-const reference)
{
return m_string;
}
operator std::string&& () && // rvalue only
{
return std::move( m_string );
}
But this breaks conversion from const StringDecorator, making its usage awkward in typical cases.
This broken solution led me thinking if there is a way to specify member function qualifier that will make conversion operator viable with const lvalue object, but not with the rvalue. And I've managed to achieve this by specifying implicit argument for const conversion operator as const volatile&:
operator const std::string& () const volatile& // lvalue only (rvalue can't bind to volatile reference)
{
return const_cast< const StringDecorator* >( this )->m_string;
}
operator std::string&& () && // rvalue only
{
return std::move( m_string );
}
Per [dcl.init.ref]/5, for a reference to be initialized by binding to
an rvalue, the reference must be a const non-volatile lvalue
reference, or an rvalue reference:
While lvalue reference and const lvalue reference can bind to const volatile reference. Obviously volatile modifier on member function serves completely different thing. But hey, it works and sufficient for my use-case. The only remaining problem is that code becomes misleading and astonishing.
clang++ is more accurate. Both func overloads are not exact matches for StringDecorator const& or StringDecorator&&. Thus my_string can not be moved. The compiler can not choose between possible transforms StringDecorator&& --> std::string&& --> func(std::string&&) and StringDecorator&& -> StringDecorator& --> std::string& --> func(const std::string&). In other words the compiler can not determine on what step it should apply cast operator.
I do not have g++ installed to check my assumption. I guess it goes to the second way, since my_string can not be moved, it applies the cast operator const& to StringDecorator&. You can check it if you add debug output into bodies of cast operators.
class myClass {
int arr[100];
public:
void *get(long i, void* const to) const;
void *get(long i, bool nog);
void *tstfn(void* const to) { return get(0L,to); }
};
gcc -Wall says:
dt.cpp: In member function ‘void* myClass::tstfn(void*)’:
dt.cpp:6:49: warning: ISO C++ says that these are ambiguous, even though the worst conversion for the first is better than the worst conversion for the second: [enabled by default]
dt.cpp:4:9: note: candidate 1: void* myClass::get(long int, void*) const
dt.cpp:5:9: note: candidate 2: void* myClass::get(long int, bool)
Both function calls require a type conversion:
calling the void* function requires adding a const qualifer to this
calling the bool function requires converting to from void* to bool.
So, by the overload resolution rules, neither is a "better" match than the other, and the call is considered ambiguous.
Perhaps you can add const to the second function; perhaps you could remove it from the first (although I'd prefer not to); perhaps you can do an explicit type conversion of either this or to to force your preferred override.
Because void *get(long i, void* const to) is const.
This means that calling it from tstfn (which is non-const) would require qualification conversion for this from myClass* to const myClass*, so calling both functions would require a conversion for the arguments (this is treated in the same way as other arguments), so the call is ambiguous.
Simply because your testfn is a non-const function, which would call the non-const version of get. The non-const function get, takes bool not const void*. Had only one get function was there (possibly taking void* as the second argument, irrespective of its constness), then would be called without ambiguity.
Is it legal to define two class functions, one overloaded with a reference to a string and the other overloaded with a const char*?
void funcA(const std::string& s)
void funcA(const char* s)
Can I guarantee that if I call funcA() with a const char* input, it will not call the string function? I'm asking because there is an implicit construction from a const char* to a string.
Yes, it's valid. If you pass a const char*, the second overload is an exact match, which is preferred over all other overloads, particularly ones involving user-defined conversions (such as converting to a const std::string).
I don't understand why this program produces the output below.
void blah(const char* ) {printf("const char*\n");}
void blah(const std::string&) {printf("const string ref\n");}
template<class t>
void blah(t) {printf ("unknown\n");}
int main(int, char*)
{
blah("hi");
char a[4];
blah(a);
std::string s;
blah(s);
getch();
}
Outputs:
const char*
unknown
const string
In VS2008. It is willing to convert the std::string to a const reference, but why won't it convert the char* to a const char* and use the overload?
The type of "hi" is const char[3], whereas the type of a is char[4].
So, the first call requires only array-to-pointer conversion (aka "decay"). The third call requires only binding an object to a reference-to-const (I don't think "converting" is the correct terminology for reference-binding, although I may be mistaken). The second call would require array decay and a pointer conversion in order to call the const char* overload.
I claim without actually checking the overload resolution text in the standard that this extra step is what makes the template a better match than the const char* overload.
Btw, if you change "unknown\n" to "%s\n", typeid(t).name() then you can see what the type t was deduced as. For your code, it is deduced as char* (because arrays can't be passed by value), but see what happens if you change the template to take a t& parameter instead of t. Then t can be deduced as char[4].