Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Can returning a local variable by value in C++11/14 result in the return value being constructed by rvalue when no copy/move is involved?

Tags:

c++

c++11

c++14

I know that in the following situation that the compiler is free to move-construct the return value from makeA (but is also free to elide the copy or move altogether):

struct A
{
    A(A&);
    A(A&&);
};

A makeA()
{
    A localA;
    return localA;
}

What I wonder is whether the compiler is allowed to construct an object of type A from a local object of type B by rvalue reference if it is being constructed in the return statement. In other words, in the following example, is the compiler allowed to select A's constructor 4 for the return value?

struct B { };
struct A {
    A(A&);  // (1)
    A(A&&); // (2)
    A(B&);  // (3)
    A(B&&); // (4)
};

A makeA()
{
    B localB;
    return localB;
}

I ask this because it would seem to me that the same logic that allows a local object of type A to be treated as an rvalue in the return statement should also allow a local of any type to be treated as an rvalue, but I cannot find any examples or questions of this nature.

like image 227
IntrepidCuriosity Avatar asked Sep 16 '14 18:09

IntrepidCuriosity


People also ask

Can local variables be returned?

How to return a local variable from a function? But there is a way to access the local variables of a function using pointers, by creating another pointer variable that points to the variable to be returned and returning the pointer variable itself.

Can we return reference of local variable in C++?

Returning values by reference in C++ A C++ function can return a reference in a similar way as it returns a pointer. When returning a reference, be careful that the object being referred to does not go out of scope. So it is not legal to return a reference to local var.

Is it unsafe for a function to return a local variable by reference?

It is dangerous to have a dangling pointer like that as pointers or references to local variables are not allowed to escape the function where local variables live; hence, the compiler throws an error.

What is returning value from function in C++?

In C++, a function which is defined as having a return type of void , or is a constructor or destructor, must not return a value. If a function is defined as having a return type other than void , it should return a value.


1 Answers

The rule for this situation changed between 2011 and 2014. The compiler should now treat localB as an rvalue.

The applicable rule for return statements is found in §12.8 [class.copy]/p32, which reads in C++14 (quoting N3936, emphasis mine):

When the criteria for elision of a copy/move operation are met, but not for an exception-declaration, and the object to be copied is designated by an lvalue, or when the expression in a return statement is a (possibly parenthesized) id-expression that names an object with automatic storage duration declared in the body or parameter-declaration-clause of the innermost enclosing function or lambda-expression, overload resolution to select the constructor for the copy is first performed as if the object were designated by an rvalue. If the first overload resolution fails or was not performed, or if the type of the first parameter of the selected constructor is not an rvalue reference to the object’s type (possibly cv-qualified), overload resolution is performed again, considering the object as an lvalue.

The bolded clause was added by CWG issue 1579, expressly to require the converting move constructor A::A(B&&) to be called here. This is implemented in GCC 5 and Clang 3.9.

Back in 2011, this "try rvalue first" rule was closely tied to the criteria for copy elision (quoting N3337):

When the criteria for elision of a copy operation are met or would be met save for the fact that the source object is a function parameter, and the object to be copied is designated by an lvalue, overload resolution to select the constructor for the copy is first performed as if the object were designated by an rvalue.

Since copy elision necessarily requires the two to have the same type, this paragraph didn't apply, and the compiler had to use the A::A(B&) constructor.

Note that as CWG 1579 is considered a DR against C++11, compilers should implement its resolution even in C++11 mode.

like image 194
T.C. Avatar answered Oct 10 '22 21:10

T.C.