Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Constructor from rvalue reference to base - viable (gcc) or not (clang) - who's right

Had a compliation issue recently, illustrated by this snippet:

struct Base
{
};

template<typename T>
struct A : Base
{
    A(){}
    A(Base&&) {}
};

A<int> foo()
{
    A<double> v;
    return v;
}


int main()
{
    auto d = foo();
    return 0;
}

Gcc says it's ok, but clang disagrees and says "candidate constructor not viable: no known conversion from 'A' to 'Base &&' for 1st argument A(Base&&) {}", see for yourself: https://godbolt.org/z/Y7mwnU

Would any of the kind readers be able to help with some standardese to support either point of view?

like image 246
Łukasz Wojakowski Avatar asked Oct 05 '18 09:10

Łukasz Wojakowski


1 Answers

clang is correct here. Filed 87530.

The rule for return statements is [class.copy.elision]/3:

In the following copy-initialization contexts, a move operation might be used instead of a copy operation:

  • If the expression in a return statement ([stmt.return]) 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, or
  • if the operand of a throw-expression is the name of a non-volatile automatic object (other than a function or catch-clause parameter) whose scope does not extend beyond the end of the innermost enclosing try-block (if there is one),

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. [ Note: This two-stage overload resolution must be performed regardless of whether copy elision will occur. It determines the constructor to be called if elision is not performed, and the selected constructor must be accessible even if the call is elided. — end note ]

Emphasis mine.

We meet the first bullet, we're returning an id-expression that names a non-volatile automatic object. So we perform overload resolution as if it were an rvalue. This overload resolution succeeds, there is a constructor that takes a Base&&. However, note the bolded part. The type of this parameter is not an rvalue reference to the object's type.

Hence, we try again consider the object as an lvalue. This overload resolution fails.

As a result, the program is ill-formed.

like image 141
Barry Avatar answered Oct 13 '22 13:10

Barry