为什么std :: tuple不能分配初始化列表?

时间:2016-11-16 11:33:18

标签: c++11 stdtuple

我想知道为什么要做出这个选择。它将允许以非常清晰和整洁的方式编写许多函数..例如:

int greatestCommonDivisor(int a, int b)
{
    if (b > a)
        std::tie(a, b) = { b, a };
    while (b > 0)
        std::tie(a, b) = { b, a % b };
    return a;
}

2 个答案:

答案 0 :(得分:5)

std::initializer_list同类项目集合,而std::tuple异构。为std::initializer_list定义std::tuple::operator=有意义的唯一情况是元组是同构的并且与初始化列表具有相同的大小,这种情况很少发生。

(Additional information in this question.)

解决方案/解决方法:您可以改为使用std::make_tuple

int greatestCommonDivisor(int a, int b)
{
    if (b > a)
        std::tie(a, b) = std::make_tuple(b, a);
    while (b > 0)
        std::tie(a, b) = std::make_tuple(b, a % b);
    return a;
}

...或{+ 1}} C ++ 17 中的构造函数(感谢Template argument deduction for class templates

std::tuple

答案 1 :(得分:2)

为什么

std::tie(a,b) = {b, a};

不编译?

赋值右侧的

{}只能调用operator=参数的非显式构造函数。

可用的operator=重载是:

tuple& operator=( const tuple& other );
tuple& operator=( tuple&& other );
template< class... UTypes >
tuple& operator=( const tuple<UTypes...>& other );
template< class... UTypes >
tuple& operator=( tuple<UTypes...>&& other );
template< class U1, class U2 >
tuple& operator=( const pair<U1,U2>& p );
template< class U1, class U2 >
tuple& operator=( pair<U1,U2>&& p );

template运算符重载无法从{}中推断出它们的类型(注意:这可能会在C ++ 17中发生变化),只留下:

tuple& operator=( const tuple& other );
tuple& operator=( tuple&& other );

在这种情况下,tuplestd::tuple<int&, int&>

完美转发元素构造的tuple<Ts...>的元组构造函数是explicit(该列表中的#3)。 {}不会调用显式构造函数。

有条件的非显式构造函数需要Ts const&...;如果Ts不可复制,且int&不可复制,则不存在。

因此,没有可行的类型可以从{int&, int&}构造,并且重载解析失败。

为什么标准不解决这个问题?好吧,我们可以自己做!

为了解决这个问题,我们必须向(Ts...)添加一个特殊的tuple非显式构造函数,只有在Ts类型都是引用时才存在。

如果我们写一个玩具元组:

struct toy {
  std::tuple<int&, int&> data;
  toy( int& a, int& b ):data(a,b) {} // note, non-explicit!
};
toy toy_tie( int& a, int& b ) { return {a,b}; }

并使用它,你会注意到

    std::tie(a, b) = {b, a};

编译并运行。

然而,

    std::tie(a, b) = { b, a % b };

没有,因为a%b无法绑定到int&

然后我们可以使用以下内容增加toy

template<class...>
toy& operator=( std::tuple<int, int> o ) {
    data = o;
    return *this;
}

(+默认的特殊成员函数。template<class...>确保它的优先级低于特殊成员函数,因为它应该如此。

这允许从{int,int}分配。然后我们运行它......得到错误的结果。 5,20的gcd是20。出了什么问题?

  toy_tie(a, b) = std::tie( b, a );

绑定到引用的ab都不是安全的代码,这就是

  toy_tie(a, b) = { b, a };

确实

简而言之,做这件事很棘手。在这种情况下,您需要先进行右侧的复制,然后才能安全地进行分配。知道何时复制以及何时不复制也很棘手。

隐含地执行此工作看起来容易出错。因此,从某种意义上说,它是偶然的,它不起作用,但修复它(虽然可能)看起来是个坏主意。

live example