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

jha*_*a-G 8 c++ tuples c++11

我有一个复制和移动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;
};
Run Code Online (Sandbox Code Playgroud)

我想用这个类的对象创建一个元组.但以下不编译:

auto p = std::tuple<A,A>(A{10},A{20}); 
Run Code Online (Sandbox Code Playgroud)

在另一方面,下面做编译,但给出了一个惊人的输出.

int main() {
    auto q = std::tuple<A&&,A&&>(A{100},A{200});
    std::cout << "q created\n";
}
Run Code Online (Sandbox Code Playgroud)

产量

~A()0x22fe10 : 100
~A()0x22fe30 : 200
q created
Run Code Online (Sandbox Code Playgroud)

这意味着一旦元组构造线结束,就会调用对象的dtor.那么,被毁物体元组的意义何在?

Bar*_*rry 11

这是不好的:

auto q = std::tuple<A&&,A&&>(A{100},A{200});
Run Code Online (Sandbox Code Playgroud)

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

正确的陈述是:

std::tuple<A, A> q(100, 200);
Run Code Online (Sandbox Code Playgroud)

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

template <class... UTypes>
  constexpr explicit tuple(UTypes&&... u);
Run Code Online (Sandbox Code Playgroud)

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

所以,这个构造函数没有参与重载解析,因为int它不能隐式转换为A.这已经通过改进pair并tuple解决了您的用例:

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

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

备注:除非sizeof...(Types) >= 1并且is_constructible<Ti, Ui&&>::value对所有人都适用,否则i此构造函数不应参与重载决策.构造函数是明确的,当且仅当is_convertible<Ui&&, Ti>::value是false至少一个我.

而且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);
}
Run Code Online (Sandbox Code Playgroud)

如果USE_OLD_RULES已定义,则第一个构造函数是唯一可行的构造函数,因此代码将无法编译,因为它D是不可复制的.否则,第二个构造函数是最可行的候选者,而且其中一个构造良好.


最近采用的是gcc 5.2和clang 3.6实际上都不会编译这个例子.所以你需要一个比这更新的编译器(gcc 6.0工作)或者想出一个不同的设计.