NGI NGI - 4 years ago 149
C++ Question

Is constructed string("Plain Old C chain") a rvalue?

I was wondering if a copy elision is made in the call of foo(string) below.
(Note: foo(string) belongs to an interface that I can not change ).

For this I attempted to check whether constructed string("Hello world!") is a rvalue.

I search on SO how to do this programmatically and found this post

void foo( string str)
{
cout << str << endl;
}

int main()
{
foo("Hello world!");
cout << is_rvalue_reference<decltype(string("Hello world!"))>::value << endl;
}


result is

Hello world!
0


I thought I would get true to is_rvalue_reference< xxx >::value


  • Where am I wrong?

  • string("Hello world!") may be a rvalue but does not seem to be a "reference of any kind" (either lvalue, rvalue, universal ... ) so that I got false result. Is there a way to get a true answer in case of a rvalue ?

  • Is there copy elision or not in his example ?


Answer Source
  • Where am I wrong?

std::string("") is an rvalue, but decltype(std::string("")) is not an rvalue reference. The type of a std::string object is ... std:string, of course.

You have a category error. An rvalue is a kind of expression, an rvalue reference is a kind of type.

A temporary string object is an rvalue. The type string&& is an rvalue reference type.

Your decltype expression is not useful for what you're trying to do. Consider:

std::string s;
using type1 = decltype(s);
using type2 = decltype(std::string(""));
static_assert(std::is_same<type1, type2>::value, "same");

In both these cases decltype gives the same type: std::string. That's because decltype tells you about types, not value categories (i.e. whether an expression is an rvalue or an lvalue).

If you want to know whether an expression is an rvalue or an lvalue you need to know more than just its type. In the case of decltype(std::string("")) you are creating an unnamed temporary, which is an rvalue. You don't need to ask its type to know that.

  • string("Hello world!") may be a rvalue but does not seem to be a "reference of any kind" (either lvalue, rvalue, universal ... ) so that I got false result. Is there a way to get a true answer in case of a rvalue ?

What do you mean by "true" answer? Do you just mean a type trait that will give the result true?

You can ask if the type is convertible to an rvalue reference:

std::is_convertible<decltype(std::string("")), std::string&&>::value

That will tell you if you can bind an rvalue reference to the object. But it's a silly question: of course you can bind an rvalue reference of type X&& to a temporary of type X. You would never need to ask that.

And anyway, you function doesn't take an argument of type string&& so asking that question doesn't even tell you anything about your call to foo(std::string).

  • Is there copy elision or not in his example ?

Yes, initializing the function argument from a temporary string should not make any copies or moves, they should be elided. The C++14 standard says in [class.copy] p31 that a copy/move can be elided when a temporary object (that has not been bound to a reference) would be copied/moved to a class object of the same type. That condition is met when initializing a function argument of class type from a temporary of the same type. A compiler that doesn't perform that elision (at least when optimisations are enabled, or in "release" builds) is a bad compiler.

There is an explanation of the copy elision rules at http://en.cppreference.com/w/cpp/language/copy_elision -- see the part about a nameless temporary.

Recommended from our users: Dynamic Network Monitoring from WhatsUp Gold from IPSwitch. Free Download