为什么 enable_if 的模板构造函数中的可选参数可以帮助编译器推导出模板参数?
Why does an optional argument in a template constructor for enable_if help the compiler to deduce the template parameter?
最小的例子很短:
#include <iostream>
#include <array>
#include <type_traits>
struct Foo{
//template <class C>
//Foo(C col, typename std::enable_if<true,C>::type* = 0){
// std::cout << "optional argument constructor works" << std::endl;
//}
template <class C>
Foo(typename std::enable_if<true, C>::type col){
std::cout << "no optional argument constructor works NOT" << std::endl;
}
};
int main()
{
auto foo = Foo(std::array<bool,3>{0,0,1});
}
第一个构造函数按预期工作。但是第二个构造函数没有编译,我得到
error: no matching function for call to ‘Foo::Foo(std::array)’
然而给出的解释
note: template argument deduction/substitution failed
没有帮助,因为 std::enable_if<true, C>::type
应该是 C
这样两个构造函数中的第一个参数在编译器看来应该完全相同。我显然错过了一些东西。为什么编译器的行为不同,对于不使用可选参数的构造函数和 enable_if 是否有任何其他解决方案?
完整的错误信息:
main.cpp:18:45: error: no matching function for call to ‘Foo::Foo(std::array)’
18 | auto foo = Foo(std::array<bool,3>{0,0,1});
| ^
main.cpp:11:5: note: candidate: ‘template Foo::Foo(typename std::enable_if::type)’
11 | Foo(typename std::enable_if<true, C>::type col){
| ^~~
main.cpp:11:5: note: template argument deduction/substitution failed:
main.cpp:18:45: note: couldn’t deduce template parameter ‘C’
18 | auto foo = Foo(std::array<bool,3>{0,0,1});
| ^
main.cpp:5:8: note: candidate: ‘constexpr Foo::Foo(const Foo&)’
5 | struct Foo{
| ^~~
main.cpp:5:8: note: no known conversion for argument 1 from ‘std::array’ to ‘const Foo&’
main.cpp:5:8: note: candidate: ‘constexpr Foo::Foo(Foo&&)’
main.cpp:5:8: note: no known conversion for argument 1 from ‘std::array’ to ‘Foo&&’
模板参数推导失败,因为 C
出现在 Non-deduced context 中。链接页面列表
The nested-name-specifier (everything to the left of the scope resolution operator ::) of a type that was specified using a qualified-id
作为 non-deduced 上下文。
他们还进一步提到了另一个例子:
For example, in A<T>::B<T2>
, T
is non-deduced because of rule #1 (nested name specifier), and T2
is non-deduced because it is part of the same type name, but in void(*f)(typename A<T>::B, A<T>)
, the T
in A<T>::B
is non-deduced (because of the same rule), while the T
in A<T>
is deduced.
模板参数推导不能这样工作。
假设您有一个模板和一个使用该模板的类型别名的函数:
template <typename T>
struct foo;
template <typename S>
void bar(foo<S>::type x) {}
当您调用该函数时,例如 foo(1)
,那么编译器将不会尝试 foo
的所有实例化以查看是否有一个 type
与 [=15] 的类型相匹配=].它不能这样做,因为 foo::type
不一定是明确的。可能是不同的实例化具有相同的 foo<T>::type
:
template <>
struct foo<int> { using type = int; };
template <>
struct foo<double> { using type = int; };
而不是尝试这条路线并可能导致歧义,foo<S>::type x
是一个非推导的上下文。有关详细信息,请参阅 What is a nondeduced context?。
其他答案已经解释了为什么参数推导在这里不起作用。如果你想 enable_if
你的构造函数,你可以简单地将条件放在模板列表中,如下所示:
struct Foo{
// your condition here ---v
template <class C, typename std::enable_if_t< true >* = nullptr>
Foo(C col) {
std::cout << "constructor" << std::endl;
}
};
最小的例子很短:
#include <iostream>
#include <array>
#include <type_traits>
struct Foo{
//template <class C>
//Foo(C col, typename std::enable_if<true,C>::type* = 0){
// std::cout << "optional argument constructor works" << std::endl;
//}
template <class C>
Foo(typename std::enable_if<true, C>::type col){
std::cout << "no optional argument constructor works NOT" << std::endl;
}
};
int main()
{
auto foo = Foo(std::array<bool,3>{0,0,1});
}
第一个构造函数按预期工作。但是第二个构造函数没有编译,我得到
error: no matching function for call to ‘Foo::Foo(std::array)’
然而给出的解释
note: template argument deduction/substitution failed
没有帮助,因为 std::enable_if<true, C>::type
应该是 C
这样两个构造函数中的第一个参数在编译器看来应该完全相同。我显然错过了一些东西。为什么编译器的行为不同,对于不使用可选参数的构造函数和 enable_if 是否有任何其他解决方案?
完整的错误信息:
main.cpp:18:45: error: no matching function for call to ‘Foo::Foo(std::array)’
18 | auto foo = Foo(std::array<bool,3>{0,0,1});
| ^
main.cpp:11:5: note: candidate: ‘template Foo::Foo(typename std::enable_if::type)’
11 | Foo(typename std::enable_if<true, C>::type col){
| ^~~
main.cpp:11:5: note: template argument deduction/substitution failed:
main.cpp:18:45: note: couldn’t deduce template parameter ‘C’
18 | auto foo = Foo(std::array<bool,3>{0,0,1});
| ^
main.cpp:5:8: note: candidate: ‘constexpr Foo::Foo(const Foo&)’
5 | struct Foo{
| ^~~
main.cpp:5:8: note: no known conversion for argument 1 from ‘std::array’ to ‘const Foo&’
main.cpp:5:8: note: candidate: ‘constexpr Foo::Foo(Foo&&)’
main.cpp:5:8: note: no known conversion for argument 1 from ‘std::array’ to ‘Foo&&’
模板参数推导失败,因为 C
出现在 Non-deduced context 中。链接页面列表
The nested-name-specifier (everything to the left of the scope resolution operator ::) of a type that was specified using a qualified-id
作为 non-deduced 上下文。
他们还进一步提到了另一个例子:
For example, in
A<T>::B<T2>
,T
is non-deduced because of rule #1 (nested name specifier), andT2
is non-deduced because it is part of the same type name, but invoid(*f)(typename A<T>::B, A<T>)
, theT
inA<T>::B
is non-deduced (because of the same rule), while theT
inA<T>
is deduced.
模板参数推导不能这样工作。
假设您有一个模板和一个使用该模板的类型别名的函数:
template <typename T>
struct foo;
template <typename S>
void bar(foo<S>::type x) {}
当您调用该函数时,例如 foo(1)
,那么编译器将不会尝试 foo
的所有实例化以查看是否有一个 type
与 [=15] 的类型相匹配=].它不能这样做,因为 foo::type
不一定是明确的。可能是不同的实例化具有相同的 foo<T>::type
:
template <>
struct foo<int> { using type = int; };
template <>
struct foo<double> { using type = int; };
而不是尝试这条路线并可能导致歧义,foo<S>::type x
是一个非推导的上下文。有关详细信息,请参阅 What is a nondeduced context?。
其他答案已经解释了为什么参数推导在这里不起作用。如果你想 enable_if
你的构造函数,你可以简单地将条件放在模板列表中,如下所示:
struct Foo{
// your condition here ---v
template <class C, typename std::enable_if_t< true >* = nullptr>
Foo(C col) {
std::cout << "constructor" << std::endl;
}
};