std :: tuple带有move-constructable元素的默认构造函数

Moo*_*oys 5 c++ stdtuple c++17

我试图返回一个std::tuple包含不可复制构造类型的元素的。这似乎阻止了我使用默认的类构造函数构造元组。例如,要返回一个包含的元组Foofoo必须创建一个实例并创建std::moved:

class Foo {
  public:
    Foo(const Foo&) = delete;
    Foo(Foo&&) = default;
    int x;
};

tuple<int, Foo> MakeFoo() {
    Foo foo{37};
//  return {42, {37}}; // error: could not convert ‘{42, {37}}’ from ‘’ to ‘std::tuple’
    return {42, std::move(foo)};
}
Run Code Online (Sandbox Code Playgroud)

另一方面,如果将类定义为具有副本构造函数,则元组的构造可以正常工作:

class Bar {
  public:
    Bar(const Bar&) = default;
    int x;
};

tuple<int, Bar> MakeBar() {
    return {42, {37}}; // compiles ok
}
Run Code Online (Sandbox Code Playgroud)

有没有办法MakeBarFoo类中使用语法?

Mar*_*ark 2

不完全是,但你(至少)有两个选择。

您可以拼出Foo

tuple<int, Foo> MakeFoo() {
    return {42, Foo{37}}
}
Run Code Online (Sandbox Code Playgroud)

或者您可以添加一个构造函数来Foo替换您现在正在执行的聚合初始化:

class Foo {
  public:
    Foo(const Foo&) = delete;
    Foo(Foo&&) = default;
    Foo(int x) : x(x) {}      // <--
    int x;
};

tuple<int, Foo> MakeFoo() {
    return {42, 37};
}
Run Code Online (Sandbox Code Playgroud)

但为什么要写return {42, 37}而不是呢return {42, {37}}?为什么需要添加构造函数才能完成这项工作?

构造一个至少具有一种仅移动类型的元组意味着我们不能使用直接构造函数

tuple<Types...>::tuple(const Types &...)
Run Code Online (Sandbox Code Playgroud)

相反,我们必须使用模板化转换构造函数

template<class... UTypes>
tuple<Types...>::tuple(UTypes &&...)
Run Code Online (Sandbox Code Playgroud)

这样就可以推导出两个参数的类型。但是,{37}是一个初始值设定项列表,在本例中,它使第二个函数参数成为非推导上下文(请参阅[temp.deduct.type]/5.6)。因此模板参数推导失败,并且构造函数无法被调用。所以,我们必须这样写return {42, 37}才能让推演成功。

此外,当参数类型可转换为相应的元组元素类型时,此模板化转换构造函数仅参与重载决策。并且可转换性不考虑聚合初始化,因此 anint不能转换为原始Foo. 但是,如果我们添加转换构造函数Foo::Foo(int)int现在可以转换为Foo,并且tuple可以调用构造函数。