gpt4 book ai didi

c++ - std::tuple 用于不可复制和不可移动的对象

转载 作者:可可西里 更新时间:2023-11-01 17:36:49 25 4
gpt4 key购买 nike

我有一个删除了复制和移动构造函数的类。

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

你正在构建一个 tuple对在表达式末尾被破坏的临时对象的右值引用,所以你留下了悬空引用。

正确的说法是:

std::tuple<A, A> q(100, 200);

但是,直到最近,标准才支持上述内容。在 N4296 中,围绕 tuple 的相关构造函数的措辞是 [tuple.cnstr]:

template <class... UTypes>
constexpr explicit tuple(UTypes&&... u);

Requires: sizeof...(Types) == sizeof...(UTypes). is_constructible<Ti, Ui&&>::value is true for all i.
Effects: Initializes the elements in the tuple with the corresponding value in std::forward<UTypes>(u).
Remark: This constructor shall not participate in overload resolution unless each type in UTypes is implicitly convertible to its corresponding type in Types.

因此,此构造函数未参与重载决策,因为 int不能隐式转换为 A .这已通过采用 Improving pair and tuple 得到解决。 ,它恰好解决了您的用例:

struct D { D(int); D(const D&) = delete; };    
std::tuple<D> td(12); // Error

此构造函数的新措辞来自 N4527:

Remarks: This constructor shall not participate in overload resolution unless sizeof...(Types) >= 1 and is_constructible<Ti, Ui&&>::value is true for all i. The constructor is explicit if and only if is_convertible<Ui&&, Ti>::value is false for at least one i.

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/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com