c++11 - initialize std::string from char* directly with {} constructor - c++

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.

Related

C++: Pass string literal or variable to function

I have a function f that takes a string as input. I usually want to provide a string literal, e.g., f("hello"). However, I want to implement another function g that builds upon f:
std::string f(const std::string&& x) {
return x + " world";
}
std::string g(const std::string&& x) {
std::string res = f(x); // problem: rvalue reference to std::string cannot bind to lvalue of type std::string
res += "!";
return res;
}
int main() {
std::string res_a = f("hello");
std::string res_b = g("world");
return 0;
}
How can I achieve this in C++11/14 in a way that I can use f with string literals as well as variables?
A generic way of solving the problem of a function taking both l-value and r-value references is to use templated functions like so-
template <typename T>
T f(T&& val) {
}
template <typename T>
T g(T&& val) {
T some_val = f(std::forward<T>(val));
}
std::foward<T>(val) forwards an l-value as an l-value and an r-value as an r-value, just as its name implies.
By templating the function, you ensure that this logic works for any type and not just strings.
The traditional way to take a read-only parameter is by const lvalue reference.
std::string f(const std::string& x)
This rule of thumb applies to many types, not just std::string. The primary exceptions are types that are not bigger than a pointer (e.g. a char).
It's rather unusual for a function to have a const rvalue reference. As you discovered, that adds difficulty when trying to pass a variable as the argument. A non-const rvalue reference has value, but a const rvalue reference is inferior to a const lvaue reference in most cases. See also Do rvalue references to const have any use?

Behavior of function overloading with const

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.

Implicit conversion to const string reference fails

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));

passing a const char instead of a std::string as function argument

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";

how to initialize function arguments that are classes with default value

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())