我有一个类,删除了复制和移动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/

10-13 00:01