Search code examples
c++language-lawyerc++20lvalue-to-rvalue

Shall structured binding be returned from a function as rvalue in C++20?


Consider a C++20 program where in function foo there is a structured binding auto [y]. The function returns y, which is converted in object of type A. A can be constructed either from const reference of from rvalue-reference.

#include <tuple>
#include <iostream>

struct A {
    A(const int &) { std::cout << "A(const int &) "; }
    A(int &&) { std::cout << "A(int &&) "; }
};

A foo() {
    auto [y] = std::make_tuple(1);
    return y;
}

int main() { foo(); }

Which one of the constructors shall be selected according to C++20 language standard?

Clang selects A(const int &) and GCC selects A(int &&), demo: https://gcc.godbolt.org/z/5q779vE6T

Does one of the compilers not support yet the standard in that respect?


Solution

  • I believe that Clang is correct.

    TL;DR: some lvalues can be implicitly moved, but a structured binding is not such a lvalue.

    1. The name of a structured binding is an lvalue:

    [dcl.struct.bind]/1:

    A structured binding declaration introduces the identifiers v0, v1, v2,… of the identifier-list as names of structured bindings.

    [dcl.struct.bind]/4:

    Each vi is the name of an lvalue of type Ti that refers to the object bound to ri; the referenced type is ri.

    1. An variable name (which is normally an lvalue) can be moved in a return statement if it names an implicitly movable entity:

    An implicitly movable entity is a variable of automatic storage duration that is either a non-volatile object or an rvalue reference to a non-volatile object type. In the following copy-initialization contexts, a move operation is first considered before attempting a copy operation:

    • If the expression in a return ([stmt.return]) or co_­return ([stmt.return.coroutine]) statement is a (possibly parenthesized) id-expression that names an implicitly movable entity declared in the body or parameter-declaration-clause of the innermost enclosing function or lambda-expression, or
    • [...]
    1. As can be seen in the definition of implicitly movable entity, only objects and (rvalue) references can be implicitly moved. But a structured binding is neither.

    [basic.pre]/3:

    An entity is a value, object, reference, [or] structured binding[...].

    So I believe that a structured binding cannot be implicitly moved.

    If y were an object or reference, then it would be implicitly movable in return y;.


    Edit: C++17 as written specified that structured bindings to tuple members are references. This was corrected by CWG 2313.