为什么
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 );
在这种情况下,tuple 是 std::tuple<int&, int&>。
tuple<Ts...> 的元组构造函数完美转发元素构造是explicit(该列表中的#3)。 {} 不会调用显式构造函数。
条件非显式构造函数采用Ts const&...;如果Ts 不可复制,并且int& 不可复制,则它不存在。
因此没有可从{int&, int&} 构造的可行类型,重载解析失败。
为什么标准没有解决这个问题?好吧,我们可以自己做!
为了解决这个问题,我们必须向 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&。
然后我们可以增加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 );
a 和 b 都绑定到引用不是安全代码,这就是
toy_tie(a, b) = { b, a };
会。
简而言之,正确执行此操作很棘手。在这种情况下,为了安全起见,您需要在分配之前获取右侧的副本。知道什么时候复制什么时候不复制也很棘手。
让这项工作隐含地看起来容易出错。因此,从某种意义上说,它不起作用是偶然的,但修复它(虽然可能)看起来是个坏主意。
live example.