【问题标题】:Why can a std::tuple not be assigned with an initializer list?为什么不能为 std::tuple 分配初始化列表?
【发布时间】:2016-11-16 11:33:18
【问题描述】:

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

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;
}

【问题讨论】:

    标签: c++11 stdtuple


    【解决方案1】:

    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;
    }
    

    ...或std::tuple 在 C++17 中的构造函数(感谢Template argument deduction for class templates

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

    【讨论】:

    • ={ blah } 并不总是引用初始化列表;它可以是建筑。
    【解决方案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&lt;int&amp;, int&amp;&gt;

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

    条件非显式构造函数采用Ts const&amp;...;如果Ts 不可复制,并且int&amp; 不可复制,则它不存在。

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


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

    为了解决这个问题,我们必须向 tuple 添加一个特殊的 (Ts...) 非显式构造函数,该构造函数仅在 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&amp;

    然后我们可以增加toy

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

    (+ 默认的特殊成员函数。template&lt;class...&gt; 确保它的优先级低于特殊成员函数,因为它应该)。

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

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

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

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

    会。

    简而言之,正确执行此操作很棘手。在这种情况下,为了安全起见,您需要在分配之前获取右侧的副本。知道什么时候复制什么时候不复制也很棘手。

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

    live example.

    【讨论】:

    • 很好的解释.. 我受到了 python 的多重赋值(例如 a, b = b, a%b)的影响,但是为了安全起见,您每次都需要复制一份(就像 Python 一样)跨度>
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-03-19
    • 2012-05-17
    • 2012-02-13
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多