std :: tuple用于不可复制和不可移动的对象 [英] std::tuple for non-copyable and non-movable object

查看:90
本文介绍了std :: tuple用于不可复制和不可移动的对象的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有一堂课,上面有&将ctor删除.

I have a class with copy & move ctor deleted.

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

我想用该类的对象创建一个元组.但是以下内容无法编译:

And I want to create a tuple with objects of this class. But the following does not compile:

auto p = std::tuple<A,A>(A{10},A{20}); 

另一方面,以下可以进行编译,但是给出令人惊讶的输出.

On the other hand, the following does compile, but gives a surprising output.

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.那么,被破坏的对象元组的重要意义是什么?

It means that dtor for objects is called as soon as tuple construction line ends. So, what is significance of a tuple of destroyed objects?

推荐答案

这很糟糕:

auto q = std::tuple<A&&,A&&>(A{100},A{200});

您正在构造一个rvalue引用的 tuple 元,这些rvalue引用是在表达式末尾被销毁的临时对象,因此剩下的是悬挂的引用.

you are constructing a tuple of rvalue references to temporaries that get destroyed at the end of the expression, so you're left with dangling references.

正确的陈述是:

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


但是,直到最近,该标准还不支持上述内容.在N4296中,有关 tuple 的相关构造函数的措辞为[tuple.cnstr]:


However, until quite recently, the above was not supported by the standard. In N4296, the wording around the relevant constructor for tuple is [tuple.cnstr]:

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

要求: sizeof ...(Types)== sizeof ...(UTypes). is_constructible< Ti,Ui&> :: value 为true对于所有 i .
效果:使用 std :: forward< UTypes>(u)中的相应值初始化元组中的元素.
备注:此构造函数不得参与重载解析,除非 UTypes 中的每种类型为可以隐式转换为类型 中的相应类型.

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 .通过采用改进 pair ,这一问题已得到解决.>和 tuple ,它们恰好解决了您的用例:

So, this constructor was not participating in overload resolution because int is not implicitly convertible to A. This has been resolved by the adoption of Improving pair and tuple, which addressed precisely your use-case:

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

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

The new wording for this constructor is, from N4527:

备注:除非 sizeof ...(Types)> == 1 is_constructible< Ti,Ui&> :: value 对于所有 i 都是正确的.构造函数是显式的,当且仅当如果至少一个 i is_convertible< Ui&&,Ti> :: value false .

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 为true.

为了以另一种方式展示差异,这是一个极其简化的元组实现:

To present the difference another way, here is an extremely stripped down tuple implementation:

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 是不可复制的,因此无法编译该代码.否则,第二个构造函数是最佳可行的候选函数,并且结构良好.

If USE_OLD_RULES is defined, the first constructor is the only viable constructor and hence the code will not compile since D is noncopyable. Otherwise, the second constructor is the best viable candidate and that one is well-formed.

这种采用是最近的,以至于gcc 5.2和clang 3.6都不会实际编译该示例.因此,您将需要比该版本更新的编译器(gcc 6.0可以运行)或提出不同的设计.

The adoption was recent enough that neither gcc 5.2 nor clang 3.6 actually will compile this example yet. So you will either need a newer compiler than that (gcc 6.0 works) or come up with a different design.

这篇关于std :: tuple用于不可复制和不可移动的对象的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆