我正在创建一个基于继承的元组,类似于 https://stackoverflow.com/a/52208842/1284735 中的完成方式,但使用构造函数除外。
我认为我的常规构造函数似乎工作正常,但是当我尝试使用复制构造函数时,编译器表示我没有向该函数提供任何参数。我错过了什么?
tuple.cpp:41:5:注意:候选构造函数不可行:需要单个参数“t”,但未提供任何参数 TupleImpl(TupleImpl& t):
template<size_t Idx, typename T>
struct TupleLeaf {
T val;
TupleLeaf() = default;
// I think should also have proper constructors but left like this for simplicity
TupleLeaf(T t): val(t) {}
};
template<size_t Idx, typename... Args>
struct TupleImpl;
template<size_t Idx, typename T, typename... Args>
struct TupleImpl<Idx, T, Args...>: TupleLeaf<Idx, T>, TupleImpl<Idx + 1, Args...> {
template<typename F, typename... Rest>
TupleImpl(F&& val, Rest&&... args): TupleLeaf<Idx, T>(std::forward<F>(val)), TupleImpl<Idx + 1, Args...>(std::forward<Rest>(args)...) {}
TupleImpl(TupleImpl& t):
TupleLeaf<Idx, T>(static_cast<TupleLeaf<Idx, T>>(t).val),
TupleImpl<Idx + 1, Args...>(t) {}
};
template<size_t Idx>
struct TupleImpl<Idx> {
TupleImpl() = default;
TupleImpl(TupleImpl<Idx> &t) {}
};
template<typename... Args>
using Tuple = TupleImpl<0, Args...>;
int main() {
Tuple<int, char, string> tup{1, 'a', "5"}; // Works okay
Tuple<int, char, string> x = tup; // Fails here
}
Tuple<int, char, std::string>
是TupleImpl<0, int, char, std::string>
的别名,推导后有两个构造函数:
TupleImpl(TupleImpl&)
,它是第一个构造函数的特化,其中 F
等于 TupleImpl&
并且 Rest
为空。TupleImpl(TupleImpl&)
。选择构造函数 #2 是因为非模板函数优于模板特化。 (就其价值而言,这不是典型的复制构造函数签名。)
此构造函数调用其基
TupleImpl<1, char, std::string>
的构造函数,并引用相同的参数(具有派生类的类型)。
推导后,又多了两个构造函数:
TupleImpl(Tuple&)
,它是第一个构造函数的特化,其中 F
等于 Tuple&
并且 Rest
为空。TupleImpl(TupleImpl&)
由于参数的类型为
Tuple
,在这种情况下源自 TupleImpl
,因此第一个是更好的匹配。
然后,此构造函数使用转发的 Tuple<2, std::string>
参数(这是一个空参数包)调用其基
Rest
的构造函数。它尝试默认构造
Tuple<2, std::string>
,但它没有默认构造函数。这就是错误的含义:
Tuple<2, std::string>
的两个构造函数都需要一个参数,而我们传递零。您可以通过使用
static_cast
执行派生到基数转换来解决此问题。然而,模板构造函数仍然太贪婪了。即使您使用
static_cast
,模板构造函数也需要受到约束,以免接管
TupleImpl(const TupleImpl&)
签名(或者相反,如果您使用更典型的复制构造函数签名)。您可以通过约束模板构造函数来解决这两个问题:
template<typename F, typename... Rest>
requires (!std::derived_from<std::decay_t<F>, TupleImpl>)
TupleImpl(F&& val, Rest&&... args): TupleLeaf<Idx, T>(std::forward<F>(val)), TupleImpl<Idx + 1, Args...>(std::forward<Rest>(args)...) {}