25

考虑一个 C++20 程序,其中函数foo中有一个结构化绑定auto [y]。该函数返回y,它被转换为类型的对象AA可以从 const 引用或 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(); }

应根据 C++20 语言标准选择哪一个构造函数?

Clang 选择A(const int &)和 GCC 选择A(int &&),演示:https ://gcc.godbolt.org/z/5q779vE6T

其中一个编译器是否不支持这方面的标准?

4

1 回答 1

6

我相信 Clang 是正确的。

TL;DR:一些左值可以隐式移动,但结构化绑定不是这样的左值。

  1. 结构化绑定的名称是一个左值:

[dcl.struct.bind]/1

结构化绑定声明将标识符列表的标识符, , ,...作为结构化绑定的名称。v0v1v2

[dcl.struct.bind]/4

每个都是类型的左值的名称,它指的是绑定到的对象;引用的类型是.viTiriri

  1. 如果变量名称(通常是左值)命名为隐式可移动实体return,则可以在语句中移动它:

隐式可移动实体是自动存储持续时间的变量,它可以是非易失性对象,也可以是对非易失性对象类型的右值引用。在以下复制初始化上下文中,在尝试复制操作之前首先考虑移动操作:

  • 如果([stmt.return]) 或([stmt.return.coroutine]) 语句中的表达式是一个(可能带括号的)id 表达式,它命名在主体或参数声明子句中声明的隐式可移动实体最里面的封闭函数或lambda-expression,或returnco_­return
  • [...]
  1. 从隐式可移动实体的定义中可以看出,只有对象和(右值)引用可以隐式移动。但结构化绑定两者都不是。

[basic.pre]/3

实体是值、对象、引用,[或]结构化绑定[...]。

所以我相信结构化绑定不能被隐式移动。

如果y是一个对象或引用,那么它将隐式可移动到return y;.


编辑:所写的 C++17 指定对元组成员的结构化绑定是引用。这已由CWG 2313纠正。

于 2021-08-26T16:04:27.980 回答