This is a newbie question but I cannot understand how it works.
Suppose I have the function like the one below
void foo(const std::string& v) {
cout << v << endl;
}
And the call below in my program.
foo("hi!");
Essentially I am passing a const char* to a function argument that is const reference to a string so I have a doubt on this call.
In order to pass an argument by reference, am I right to say that the variable must exist at least for the duration of the call? If it is so, where is created the string that is passed to the function?
I can see that it works : does it happen because the compiler creates a temporary string that is passed to the argument or the function?
does it happen because the compiler creates a temporary string that is passed to the argument or the function?
Yes, and temporaries are allowed to bind to const lvalue references. The temporary string v is alive for the duration of the function call.
Note that this is possible because std::string has a implicit converting constructor with a const char* parameter. It is the same constructor that makes this possible:
std::string s = "foo";
Related
Below is a simple example that produces errors (VS 2019):
E0461 initial value of reference to non-const must be an lvalue
C2664 'void foo(std::string &)': cannot convert argument 1 from 'std::string' to 'std::string &'
using namespace std;
void foo(string& input) {
cout << "Foo is: " << input << endl;
}
string getInput() {
return string("abc");
}
int main()
{
foo(getInput());
}
I don't understand the error. getInput() returns a string. Why can't that string be passed directly to the foo() function as a reference just like any local or member variable can be passed by reference to the foo() function? As the error message suggests, making the string argument a const makes the error go away. Why would that be?
Update: I thought about this question some more after considering the response and a couple links that I posted below. I think the core issue here is that the return value of getInput() is a temporary variable. If I had instead copied the result of getInput() into a local or global variable and then passed that to foo(), there would be no error. I think the compiler does not "like" passing the temporary variable that stores the result of getInput() by reference because how long will the temporary variable stay valid? Obviously, it must stay valid immediately after getInput() returns, but how much longer after that?
This is because a const reference can bind to both lvalue and rvalue. This makes sense as being marked as const, the value cannot be changed even if the input is an rvalue. If the value is not const, you then have the option of editing the non-const reference inside the function. This would make no sense as the input was an rvalue.
EDIT: Sorry, I asked this question without a thro understanding of references...
I seem to be getting this error when I run this code...
error: invalid initialization of non-const reference of type 'std::function&' from an rvalue of type 'main()::'
#include <bits/stdc++.h>
using namespace std ;
void printfunction(bool a, function <void()> &b)
{
if (a == true)
{
b() ;
}
}
int main()
{
int value = 45 ;
printfunction(true, [value](){cout << "The value is : " << value ;}) ;
}
But, the error disappears when I add a const before function... like this :
void printfunction(bool a,const function <void()> &b)
The thing is I would like to change the function in the function reference if needed...
Is there any other way to do this? Please let me know if it does indeed exist.
Bye,
Samuel
In printfunction call, lambda expression [value]() {...} argument must be converted to a temporary function<void()> object first.
A reference to non-const function<void()>& only binds to l-values, not temporaries (r-values).
A reference to const, on the other hand, can be bound to temporaries. Which is what you observe.
If you want to modify the std::function, then you'll need to pass a modifiable (lvalue) parameter:
int main()
{
int value = 45;
std::function f = [value](){ std::cout << "The value is : " << value ;};
printfunction(true, f);
}
What you were trying to do isn't much different from writing a function that takes a mutable reference to int (e.g. void foo(int& x)) and then complaining that you can't call foo(5). (The small difference is that the the lambda-expression is converted to a temporary std::function - but that still can't be bound to a non-const reference).
An alternative would be to change printfunction to take its argument by value rather than by reference, so that it has its own copy which it may modify. You'll have to consider the needs of the caller to decide whether that's more appropriate.
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'm working on g++ and here I have tried to overload a function by just adding const to parameter. It works fine and when it runs, it calls the function without const
Is this behavior specified in the C++ standard?
What the reason it calls the function without const
void print(const std::string& str){std::cout << "const" << str << std::endl;}
void print(std::string& str){std::cout << str << std::endl;}
int main()
{
std::string temp = "hello";
print(temp);
return 0;
}
Reference bindings are an identity category §13.3.3.1.4) but since the latter is more cv-qualified, for §13.3.3.2, the non-const is preferred (sample code from the standard):
int f(const int &);
int f(int &);
int i;
int j = f(i); // calls f(int &)
That is standard behavior. Any other behavior would lead to crazy behavior. In particular, the non-const function would not be callable at all.
const is part of method signature. Overriding works only for methods with the same signature.
This behavior was made to avoid reverse situation when you use const method of base class to call not const method of child class.
The reason is this section in [over.ics.rank]/3 where this case is explicitly covered:
Standard conversion sequence S1 is a better conversion sequence than
standard conversion sequence S2 if […] — S1 and S2 are reference
bindings (8.5.3), and the types to which the references refer are the
same type except for top-level cv-qualifiers, and the type to which
the reference initialized by S2 refers is more cv-qualified than the
type to which the reference initialized by S1 refers.
S1 corresponds to the second overload and S2 to the first.
What the reason it calls the function without const
You always try to select the most specialized thing. That is the case in overload resolution just as it is in partial ordering of function templates. The second overload is more specialized than the first because the first can be called with arguments which the second cannot be called with - that is the basic reasoning behind this rule.
Overloading works by matching the types of the arguments, including the qualifiers. In your case temp has type std::string not const std::string. You have only initialised it with a literal constant, it is not itself constant.
Consider the following:
std::string temp( "hello" ) ;
print(temp); // hello
print( std::string("hello") ) ; // consthello
print( "hello" ) ; // consthello
print( static_cast<const std::string>(temp) ) ; // consthello
const std::string temp2( "hello" ) ;
print(temp2); // consthello
If you were to remove the non-const version, all three will call the remaining const overload. In this example, only the const version is in fact necessary (and preferred) since neither version modify the string object.
If on the other hand you removed the non-const version, there would be no function matching any but the first example above, and the build would fail. That is to say a non-const object can safely be passed as a const argument, but a const object cannot be passed as a non-const argument, because the function is not "promising" not to modify the object. You can force a const into a non-const argument by a const_cast as in:
const std::string temp2("hello") ;
print( const_cast<std::string&>(temp2) ) ; // hello
But if print() were to attempt to modify the object in this case the results are undefined, so consider the practice unsafe.
Making an argument const indicates intent, allows the compiler to issue a diagnostic if the code is attempts to modify the object or pass it via a non-const argument to some other function. It may also potentially provide the compiler with optimisation possibilities.
Because calling the function taking std::string const& requires two implicit conversions: one to std::string const, one to std::string const&; whereas calling the function taking std::string& requires merely one implicit conversion (to std::string&), so that one is preferred.
I'm working on Linux gcc environment and I need to initilize function arguments that are classes with default values.
When I do that with temporary instance of the class it makes an error like this: "default argument for [function argument] has type [class name].
for example:
void foo(std::wstring& str = std::wstring())
error: default argument for 'std::wstring& str' has type 'std::wstring'
P.S. this code is compiled without any error or warning with VC++.
How can I initilize the default value?
This is supposed to not compile. You are trying to bind an rvalue to a non-const reference. Say std::wstring const & str and it should work.
You could just create a function overload:
void foo() {
std::wstring str;
foo(str);
}
but I really miss the point.
EDIT:
I mean, that function's purpose is almost certainly to modify an input string. If you provide an empty input string that you can't access later, why bother?
You cannot bind non-const references to rvalues. Passing by value would work:
void foo(std::wstring str = std::wstring())
Or passing by reference-to-const:
void foo(const std::wstring& str = std::wstring())