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).
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.
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.
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.
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].
I'm confused about following program about why it calls first constructor.
class A
{
public:
A(const char *c="\0")
{
cout<<"Constructor without arg";
}
A(string c)
{
cout<<"New one";
}
};
int main()
{
A a="AMD";
return 0;
}
Output is
Constructor without arg
"AMD" is a const char[], which is implicitly converted to const char*, so the first constructor [A(const char *c="\0")]is the best match.
Note that A(const char *c="\0") is not a constructor without an argument, it's a constructor which takes a single const char* as an argument, and has an optional default value to use when a const char* isn't specified. In this case, you're passing a const char*, so it uses it.
Because a string literal is of type const char[] which implicitly converts to const char* which is preferred over the user-defined conversion std::string(const char*) (This is not really the signature of the string constructor, but enough for this explanation).
Also: initialization is not assignment. This is why a constructor and not operator= is called in the first place.
The preferred syntax for assignment in C++11 would be A a{"ASDF"};. It makes things more uniform.
You're calling the constructor with a const char * because that is what "AMD" is. It's not a string. If you put A a(string("AMD")) it will work.