为什么复制构造函数的两个版本在使用时会给出不同的结果enable_if?据我了解,使用的两个复制构造函数应该产生相同的结果(仅用于未定义Settings<int>)
template <typename Type>
struct Predicate : std::integral_constant<bool, true>
{
};
template <>
struct Predicate<int> : std::integral_constant<bool, false>
{
};
template <typename FooType>
struct Settings
{
Settings() {}
// Here it works fine
template <typename OtherFooType>
Settings(const Settings<OtherFooType>& other, std::enable_if_t<Predicate<OtherFooType>::value, int*> = 0) {}
// In this case enable_if does not work
//template <typename OtherFooType>
//Settings(typename std::enable_if<Predicate<OtherFooType>::value, const Settings<OtherFooType>&>::type other){}
};
int main()
{
Settings<float> f = Settings<char>();
return 0;
}
让我们简化一下情况:
事实上,
B<T>::type它总是等价S<T>的,但这仅适用于受过训练以解决逆问题的人。一般来说,如果B<T>在模板的实现中应用了偏特化等,那么没有明显的算法来解决“什么类型的T应该被替换为B<T>::type相等S<int>”的问题。请记住,C++ 模板形成了图灵完备的函数式编程语言,该问题相当于“使用什么初始参数集,程序将产生给定结果”的问题,它是可以解决的,仅在有限的数量内特别案例。因此,标准的作者不强制编译器的作者选择模板选项,除了14.8.2 Template argument deduction [temp.deduct](标准中的 16 页,释义:en.cppreference.com,此页面未翻译成俄文版本)。非常简短的复述:参数的类型是模板类型的社会化,在函数参数中指定的正是这个模板,然后我们选择类型(然后,我们检查继承、类型转换等)。如果不是,那么我们认为参数的类型不合适。
如果编译器从不尝试处理 B 的参数,那么为什么要让语法完全有效
void foo2(typename B<T>::type ),这意味着函数不能被调用?首先,用户可以告诉编译器:foo2<int>( S<int>{} ),其次,可以确定类型 T,例如,通过替换其他函数参数。当你写:
然后通过替换模板函数的第一个参数来确定类型 OtherFooType。由于我们正在处理一个参数,
Settings<char>我们正在尝试将该类型映射到Settings<OtherFooType>. 由于在这两种情况下都使用了 Settings 模板,因此任务被简化为匹配char和OtherFooType,并且发生了这样的匹配。由于构造函数没有第二个参数,我们使用默认值。但是第二个参数的类型已经定义了,因为它只依赖于已经定义的(当替换第一个参数时)模板参数。那些。如果只能推断类型std::enable_if_t<Predicate<OtherFooType>::value, int*>,则编译成功。考虑:
因为最终类型对模板参数的依赖很复杂,而不是简单地比较模板名称,编译器总是假定这种特化是不合适的。(除非您明确指定类型。但是,对于复制构造函数,您不能明确指定它。)
如果您不想输入虚拟参数怎么办?在 C++20 中,您可以使用 requires 代替 enable_if :
对于旧版本的 C++,您可以输入一个虚拟模板参数而不是一个虚拟函数参数:
恕我直言:萝卜辣根不甜。