Zebrafish
Zebrafish

Reputation: 13936

Are temporary objects passed to functions considered const?

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

Answers (2)

Brian Bi
Brian Bi

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 or T2 is a class type and T1 is not reference-related to T2, user-defined conversions are considered using the rules for copy-initialization of an object of type “cv1 T1” 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

Zuodian Hu
Zuodian Hu

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 const Whether or not the original temporary is 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 const apparently yes for C++17 according to the other answer. 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.

EDIT

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

Related Questions