Why does changing the function parameter of Foo and Bar from const to non-const result in different compiler errors?
Or in other words: why is Foo(false) ok, while Bar(false) result in a compiler error?
#include <string>
void Foo(const std::string &test)
{
}
void Bar(std::string &test)
{
}
int main(int , char* [])
{
Foo(false);
Bar(false); // error C2664: 'Bar' : cannot convert parameter 1 from 'bool' to 'std::string &'
return 0;
}
When you call your functions with a bool argument, the compiler will look for a viable conversion from bool to whatever type the argument has: const std::string & in the first case, and std::string & in the second case.
std::string is constructible from const char *, which false, being an integral constant 0, is implicitly convertible to (it will result in nullptr). Hence a temporary string will be constructed and bound to the const reference.
The same thing cannot be done with the non-const version of the reference, since the temporary object is not an lvalue.
Foo(false) is okay, since string has imlicit constructor, that receives const char*, so false will be converted to 0, that will be sent to c-tor.
Bar(false) is incorrect, since you cannot bind a temporary to an lvalue reference.
Because false can be converted into 0, and it's can be converted into const char * (especially NULL).
So, your code is equal to:
Foo(string((const char *)NULL));
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.
My lecture notes said
The argument to a reference parameter must be a variable, not a
constant or an expression.
And thus
int f(double & var); // function prototype
...
const double t = 4.0;
int ret = f(t);
f(t) is illegal.
But I do not understand, why would t be illegal. t is a constant, but still a variable, and I don't think there's anything wrong passing t by reference.
Let me enhance my comment to an answer:
First, t is not a constant, but a const variable. A constant would be 4.0. Your lecture notes are basically saying that you cannot do something like int ret = f(4.0);
Second, what you are seeing is a type mismatch. const as a qualifier is part of the type. You cannot do the following:
const int x = 1;
int& ref_x = x;
error: binding reference of type ‘int&’ to ‘const int’ discards
qualifiers
Nevertheless, it is legal to pass const qualified variables as reference, either use a const reference or cast away the const:
Use a const reference const int& const_int_ref = x;
Use const_cast: int& rx = const_cast<int&>(x);
I prefer the first one whereever possible.
What if the function f modifies var? That shouldn't happen if t is const.
Here's an example implementation of f:
int f(double & var)
{
var += 1;
return var;
}
This will change whatever is passed as an argument. But if the argument was const... tough luck. Then it's not allowed and the compiler explicitly tells you this.
This is the error generated by the compiler:
error: binding reference of type 'double&' to 'const double' discards qualifiers
So by passing a const variable into the function (without a non-const argument), you're telling the compiler to neglecting the constness of the variable in the first place.
If you wish to pass it by reference, pass it by const-reference:
int f(const double & var) // or int f(double const& var)
{
var += 1;
return var;
}
This tells the compiler to retain the const-ness of its arguments.
When you have a reference parameter the object passed needs to actually (at least be able to) occupy memory, a constant (as opposed to a const variable) does not.
I.E. the following would be okay:
void foo(int & n) {
n = 3;
}
void bar() {
int i;
foo(i);
std::cout << "i is " << i << std::endl;
}
but if you had:
void qux() {
foo(3);
}
there would be no object for the assignment in foo to assign to.
Note that you can pass a constant as a reference-to-const (i.e. MyType const &), that's allowed because the assignment issue does not exist when the reference is to a const.
I have a function which accepts a std::string&:
void f(std::string& s) { ... }
I have a const char* which should be the input parameter for that function. This works:
const char* s1 = "test";
std::string s2{s};
f(s2);
This doesn't:
const char* s1 = "test";
f({s1});
Why isn't this possible? The funny thing is that CLion IDE is not complaining, but the compiler is:
no known conversion for argument 1 from ‘<brace-enclosed initializer list>’ to ‘std::basic_string<char>&’
This has nothing to do with constructing std::string from char const*.
f expects a lvalue to a string, and by creating a temporary instance on the spot, you're providing an rvalue, which cannot be bound to a non-const lvalue reference. f(string{}) is just as invalid.
Your function receives a non const reference and you are passing a temporary object, which requires a copy or a const reference parameter. Two solutions, creating another function to receive the object as a rvalue reference and call the other overload within
void f(string&& s) { f(s); }
to allow temporary objects as parameter, or change your function definition to receive any object but as a constant reference
void f(const std::string& s) { ... }
One option is to change your function to take a string by value, not by reference. Then it will work. In any case, in C++11 sometimes it's preferable to pass by value, not by reference.
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].
Say I have this example:
char const * const
foo( ){
/* which is initialized to const char * const */
return str;
}
What is the right way to do it to avoid the compiler warning "type qualifier on return type is meaningless"?
The way you wrote it, it was saying "the returned pointer value is const". But non-class type rvalues are not modifiable (inherited from C), and thus the Standard says non-class type rvalues are never const-qualified (right-most const was ignored even tho specified by you) since the const would be kinda redundant. One doesn't write it - example:
int f();
int main() { f() = 0; } // error anyway!
// const redundant. returned expression still has type "int", even though the
// function-type of g remains "int const()" (potential confusion!)
int const g();
Notice that for the type of "g", the const is significant, but for rvalue expressions generated from type int const the const is ignored. So the following is an error:
int const f();
int f() { } // different return type but same parameters
There is no way known to me you could observe the "const" other than getting at the type of "g" itself (and passing &f to a template and deduce its type, for example). Finally notice that "char const" and "const char" signify the same type. I recommend you to settle with one notion and using that throughout the code.
In C, because function return values, and qualifying values is meaningless.
It may be different in C++, check other answers.
const int i = (const int)42; /* meaningless, the 42 is never gonna change */
int const foo(void); /* meaningless, the value returned from foo is never gonna change */
Only objects can be meaningfully qualified.
const int *ip = (const int *)&errno; /* ok, `ip` points to an object qualified with `const` */
const char *foo(void); /* ok, `foo()` returns a pointer to a qualified object */
None of the previous answers actually answer the "right way to do it" part of the question.
I believe that the answer to this is:
char const * foo( ){
which says you are returning a pointer a constant character.