c – 使用单参数模板化构造函数构造引用元组的值元组

我有一个const引用的元组std :: tuple< const Matrix&,...>我从中构造了一个值元组std :: tuple< Matrix,...>.对于大于1的任何大小的元组,这都可以正常工作:(在线示例:https://godbolt.org/g/24E8tU)

#include <tuple>

struct Matrix {
    Matrix() = default;
    Matrix(Matrix const&) = default;

    template <typename T>
    explicit Matrix(T const&) {
        // in reality, this comes from Eigen, and there is real work
        // being done here. this is just to demonstrate that the code
        // below fails
        static_assert(std::is_same<T, int>::value, "!");
    }
};

void works() {
    Matrix m1, m2;
    std::tuple<const Matrix &, const Matrix &> tuple_of_ref{m1, m2};

    std::tuple<Matrix, Matrix> t{tuple_of_ref};
}

但是,对于大小为1的元组,此代码无法编译:

void fails() {
    Matrix m;
    std::tuple<const Matrix &> tuple_of_ref{m};

    // Tries and fails to instantiate Matrix(std::tuple<const Matrix &>)
    std::tuple<Matrix> t{tuple_of_ref};
}

注意Matrix类有一个模板化的构造函数,它接受std :: tuple.

06002

我不想使用这个构造函数,因为它是第三方代码,所以我无法更改它.

我认为我的works()示例正确调用cppreference上列为#4的构造函数:

06003

4) Converting copy-constructor. For all i in sizeof...(UTypes), initializes ith element of the tuple with std::get<i>(other).

failed()示例尝试使用this constructor,大概是#3,我不想要:

06004

3) Converting constructor. Initializes each element of the tuple with the corresponding value in std::forward<Utypes>(args).

如何确保元组的构造函数#4用于两种情况?我的真实用例是在一个可变参数模板中,所以我不知道提前元组的大小.

最佳答案
是的,所以……这就是问题所在:

template<typename T>
explicit Matrix(const T& x)

这是一个非常不友好的构造函数 – 因为它在说谎.矩阵实际上不是可以从任何东西构建的,只是某些特定的东西 – 但是没有办法从外部检测这些东西是什么.

在考虑如何构造元组< Matrix>时从一个元组< Matrix const&>,我们有lots of choices,但实际上只有两个是可行的:

// #2, with Types... = {Matrix}
tuple(Matrix const&);

// #3, with UTypes = {tuple<Matrix const&>&}
tuple(tuple<Matrix const&>&);

两人最终都试图从元组< Matrix const&>构造一个矩阵,这不起作用而且你被卡住了.

现在,你可能会认为#4是你的救赎 – 生成一个构造函数:

tuple(tuple<Matrix const&> const& );

并从元组参数的底层Matrix构造其基础Matrix.也就是说,使用转换复制构造函数.似乎问题在于这个构造函数是有效的,但是#3因为任何原因而首选(即它需要较少的cv限定引用)并且解决方案是试图摆弄参数以便#4是首选(即在参数上使用as_const()).

但是这个构造函数并不是优先考虑的……它实际上不可行,因为that constructor is的限制(从LWG 2549开始):

either sizeof...(Types) != 1, or (when Types... expands to T and UTypes... expands to U) is_­convertible_­v<const tuple<U>&, T>, is_constructible_­v<T, const tuple<U>&>, and is_­same_­v<T, U> are all false.

但是我们确实有sizeof ..(类型)== 1并且那些东西都不是假的(尤其是第二个是真的 – 这是你开始时遇到的所有问题的根源),所以#4根本就不是候选人,并没有一个巧妙的技巧,只是让它成为一个.

那么,如何解决呢?到目前为止,最好的办法是修复Matrix.我意识到这可能不太可能,但必须要说.

您可以将Matrix包装在实际为其构造函数添加约束的内容中以避免此问题.既然你已经将它复制到一个元组中,那么你就有机会做一些简单的事情:

template <typename T>
struct only_copyable {
    only_copyable(only_copyable const& ) = default;
    only_copyable(T const& t) : t(t) { }
    template <typename U> only_copyable(U const& ) = delete;
    T t;
};

但你可能想要比这更现实的东西.你的类型也可以继承Matrix,只是为了理智而摆弄它的构造函数.

或者,在专门处理大小为1的元组时,可以避免使用元组构造函数,只需使用默认的构造/赋值.或者明确地调用get< 0>或者那种东西.

或者,您可以完全避免使用大小为1的元组.这是一个奇怪的具体事情,但也许这就足够了(或者你可以用my_tuple< A,B,C ......>是元组< A,B,C ......>来包装元组但是my_tuple< A>实际上是元组< A,monostate>).

但真的……修复Matrix构造函数似乎非常值得.

转载注明原文:c – 使用单参数模板化构造函数构造引用元组的值元组 - 代码日志