我有一个类,删除了复制和移动ctor。
struct A
{
A(int a):data(a){}
~A(){ std::cout << "~A()" << this << " : " << data << std::endl; }
A(A const &obj) = delete;
A(A &&obj) = delete;
friend std::ostream & operator << ( std::ostream & out , A const & obj);
int data;
};
我想用此类的对象创建一个元组。但是以下内容无法编译:
auto p = std::tuple<A,A>(A{10},A{20});
另一方面,以下内容确实可以编译,但是给出了令人惊讶的输出。
int main() {
auto q = std::tuple<A&&,A&&>(A{100},A{200});
std::cout << "q created\n";
}
输出
~A()0x22fe10 : 100
~A()0x22fe30 : 200
q created
这意味着在元组构造线结束时将立即调用对象的dtor。那么,被破坏的对象元组的重要意义是什么?
最佳答案
这不好:
auto q = std::tuple<A&&,A&&>(A{100},A{200});
您正在构造一个tempt的右值引用的
tuple
,这些临时值在表达式的末尾被销毁,因此留下了悬挂的引用。正确的陈述是:
std::tuple<A, A> q(100, 200);
但是,直到最近,该标准还不支持上述内容。在N4296中,有关
tuple
的相关构造函数的措辞为[tuple.cnstr]:因此,此构造函数未参与重载解析,因为
int
不能隐式转换为A
。通过使用Improving pair
and tuple
已解决了该问题,该问题恰好解决了您的用例:struct D { D(int); D(const D&) = delete; };
std::tuple<D> td(12); // Error
此构造函数的新措辞来自N4527:
is_constructible<A, int&&>::value
是正确的。为了以另一种方式展示差异,这是一个极其简化的元组实现:
struct D { D(int ) {} D(const D& ) = delete; };
template <typename T>
struct Tuple {
Tuple(const T& t)
: T(t)
{ }
template <typename U,
#ifdef USE_OLD_RULES
typename = std::enable_if_t<std::is_convertible<U, T>::value>
#else
typename = std::enable_if_t<std::is_constructible<T, U&&>::value>
#endif
>
Tuple(U&& u)
: t(std::forward<U>(u))
{ }
T t;
};
int main()
{
Tuple<D> t(12);
}
如果定义了
USE_OLD_RULES
,则第一个构造函数是唯一可行的构造函数,由于D
是不可复制的,因此代码将无法编译。否则,第二个构造函数是最佳可行的候选函数,并且结构良好。最近的采用已经足够,以至于gcc 5.2和clang 3.6都不会实际编译该示例。因此,您将需要比该版本更新的编译器(gcc 6.0可以运行)或提出不同的设计。
关于c++ - std::tuple用于不可复制和不可移动的对象,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/32763062/