#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 withstd::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 (whenTypes...
expands toT
andUTypes...
expands toU
)is_convertible_v<const tuple<U>&, T>
,is_constructible_v<T, const tuple<U>&>
, andis_same_v<T, U>
are allfalse
.
但是我们确实有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构造函数似乎非常值得.