Reputation: 13936
I've made the argument to a function const& because I want to be able to pass "string literals" to it. However I'm wondering whether it's possible to modify the reference, something like this:
void addObjectToDictionary(int* obj, const std::string& name)
{
for (int = 0; i < 10; ++i)
{
if (/*Name already exists in dictionary*/)
name = "object" + std::to_string(rand());
else { /*Add obj to dictionary*/; return; }
}
}
int main()
{
addObjectToDictionary(new int(6), "somename");
)
I'm wondering whether casting away the const on the std::string reference is undefined. Another answer on this site says:
const_cast is safe only if you're casting a variable that was originally non-const. For example, if you have a function that takes a parameter of a const char *, and you pass in a modifiable char *, it's safe to const_cast that parameter back to a char * and modify it.
I'd just like to know, however, whether the temporary std::string object created when calling the function is considered const or not.
Upvotes: 3
Views: 251
Reputation: 119219
First of all, you can use const_cast
whenever you want. It's just that if you attempt to modify an object that is actually const
, in a way the compiler can't catch at compile time, the behaviour is undefined. Using const_cast
merely increases the risk that you are going to do this.
As for your question of whether the temporary is const
in your case: the answer appears to be "yes". There have been numerous defect reports regarding the wording of reference initialization against each of the versions of the C++ standard, so I will just discuss the C++17 wording here. The relevant provision in the standard is [dcl.init.ref]/5.2.2.1:
If
T1
orT2
is a class type andT1
is not reference-related toT2
, user-defined conversions are considered using the rules for copy-initialization of an object of type “cv1T1
” by user-defined conversion (11.6, 16.3.1.4, 16.3.1.5); the program is ill-formed if the corresponding non-reference copy-initialization would be ill-formed. The result of the call to the conversion function, as described for the non-reference copy-initialization, is then used to direct-initialize the reference. For this direct-initialization, user-defined conversions are not considered.
This isn't super clear about whether the std::string
prvalue is cv-qualified, but in any case the prvalue will then be used to initialize the reference, which is governed by p5.2.1, which requires that the prvalue inherit the cv-qualifiers of the reference being initialized. So it is clear that the temporary object that is created will have type const std::string
.
So, in:
const std::string& name = "somename";
you get a const
temporary, but when you do
std::string&& name = "somename";
you get a non-const
temporary. The cv-qualification of the temporary matches that of the reference.
Upvotes: 4
Reputation: 1009
I've made the argument to a function const& because I want to be able to pass "string literals" to it.
If you're interested, the exact type of a string literal like this...
"string literal"
... is const char[15]
. It just so happens that a std::string
has a constructor that takes const char*
, which const char[15]
automatically decays to, so a string literal binds to a const std::string&
parameter.
(const char[15]) --> decay --> (const char*) --> bind --> std::string::string( const char* )
This creates a temporary std::string
, which holds a copy of the string literal. Your function then takes that temporary std::string
as a const std::string&
. The original temporary is not actually Whether or not the original temporary is const
const
is wishy-washy in the standard apparently, but it is const
in C++17, according to the other answer.
However I'm wondering whether it's possible to modify the reference
I'm wondering whether casting away the const on the std::string reference is undefined.
If you want to modify the temporary, there's no need for const_cast
. The language gives you a way to bind to temporaries in a non-const
way: the rvalue
reference.
// this is a const lvalue reference
// it binds to temporaries
void addObjectToDictionary(int* obj, const std::string& name);
// this is an rvalue reference
// it also binds to temporaries, and will bind to temporaries instead of a
// const lvalue reference if both overloads exist
void addObjectToDictionary(int* obj, std::string&& name);
The answer to your stated question...
I'd just like to know, however, whether the temporary std::string object created when calling the function is considered const or not.
... is no, temporaries are not apparently yes for C++17 according to the other answer. const
However, y You should also not take a temporary by const
lvalue reference and cast the const
away, because that signature also binds to actually const
objects. Instead, you can bind to a temporary in a non-const
way using an rvalue
reference parameter.
As a bonus, if you want to bind directly to a string literal, you can do this:
// this templates functions that bind to the exact type of any string literal
template<std::size_t N>
void addObjectToDictionary(int* obj, const char ( &name )[N] );
This template generates functions that bind to the exact types of string literals of any length. This may be a little overboard.
There's a suggestion in the comments to take the string by value (std::string
, no reference). This is also a perfectly valid way to "sink" a string into your function.
// this will bind to string literals without creating a temporary
void addObjectToDictionary(int* obj, std::string name);
What happens here, then, is a bit different. When passing a string literal to a const std::string&
parameter, you get a const
reference to a temporary std::string
. When passing a string literal to a plain old std::string
value, what used to be a temporary is now your very own std::string
value object to do with as you wish, constructed from the string literal.
Upvotes: 3