MCVE:
#include <type_traits>
template<typename T>
bool func( typename std::enable_if< std::is_enum<T>::value, T >::type &t, int x )
{
}
enum class Bar { a,b,c };
int main()
{
Bar bar{Bar::a};
func(bar, 1);
}
我希望func(bar, 1);
符合我对func
的定义,但g ++报告:
sfi.cc: In function 'int main()':
sfi.cc:13:17: error: no matching function for call to 'func(Bar&, int)'
func(bar, 1);
^
sfi.cc:13:17: note: candidate is:
sfi.cc:4:10: note: template<class T> bool func(typename std::enable_if<std::is_e
num<_Tp>::value, T>::type&, int)
bool func( typename std::enable_if< std::is_enum<T>::value, T >::type &t, int x )
^
sfi.cc:4:10: note: template argument deduction/substitution failed:
sfi.cc:13:17: note: couldn't deduce template parameter 'T'
func(bar, 1);
^
为什么这不起作用,如何解决?
背景:这是this problem
的尝试解决方案答案 0 :(得分:9)
template<typename T>
bool func( typename std::enable_if< std::is_enum<T>::value, T >::type &t, int x )
上面在非推断的上下文中使用了 T
。这意味着它不会扣除T
,因为它(在一般情况下)需要逆转任意的图灵完全转换,这是不可能的。
func
的含义是第一个参数是enum class Bar
,第二个参数是int
。从此,您希望它推导出T
。
虽然将T
设置为enum class Bar
确实可以解决问题,但C ++并不能猜测。它模式匹配。
假设我们有:
template<class T>
struct blah { using type=int; };
template<>
struct blah<int> { using type=double; };
然后
template<class T>
bool func( typename blah<T>::type );
如果有人将int
传递给func
,应该为T
推断出哪种类型?这是一个玩具示例:foo<T>::type
可以执行图灵完备算法,将T
映射到相关类型。在一般情况下,不可能反转或甚至确定逆是否是模糊的。因此,即使在简单的情况下,C ++也不会尝试,因为简单和复杂之间的边缘很快就会模糊。
解决问题:
template<class T,class=typename std::enable_if< std::is_enum<T>::value >::type>
bool func( T &t, int x ) {
}
现在T
用于推断的上下文中。 SFINAE仍然存在,但不会阻止模板类型扣除。
或者你可以等待C ++ 1z概念,它可以自动化上述结构(基本上)。
查看链接的问题,解决问题的简便方法是使用标记调度。
template<typename T>
bool func(T &t, int x)
{
// do stuff...
}
但是我希望有三个不同的功能体:
我们有3个案例:
T是一个枚举
是无符号字符
其他所有
所以,派遣:
namespace details {
template<class T>
bool func( T& t, int x, std::true_type /* is_enum */, std::false_type ) {
}
template<class T>
bool func( T& t, int x, std::false_type, std::true_type /* unsigned char */ ) {
}
template<class T>
bool func( T& t, int x, std::false_type, std::false_type ) {
// neither
}
}
template<class T>
bool func( T& t, int x ) {
return details::func( t, x, std::is_enum<T>{}, std::is_same<unsigned char, T>{} );
}
现在正常的过载规则用于在3个函数之间进行选择。如果你以某种方式拥有enum
和unsigned char
(不可能)的类型,则会出现编译时错误。
答案 1 :(得分:5)
您在非推断的上下文中使用模板参数T
。
来自§14.8.2.5/ 5 [temp.deduct.type]
未推断的背景是:
- 使用 qualified-id 指定的类型的嵌套名称说明符。
- ......
要解决此问题,请将enable_if
移至虚拟模板参数
template<typename T,
typename = typename std::enable_if< std::is_enum<T>::value, T >::type>
bool func( T &t, int x )
{
// ...
}
查看您链接的问题,您尝试根据第一个参数是func
来切换enum
的两个定义。在这种情况下,上述解决方案将无法工作,因为默认模板参数不是函数模板签名的一部分,并且最终会出现多个定义错误。
有两种不同的方法可以解决这个问题,使用虚拟模板参数
template<typename T,
typename std::enable_if< std::is_enum<T>::value, int >::type* = nullptr>
bool func( T &t, int x )
{
// ...
}
或在返回类型
中使用enable_if
表达式
template<typename T>
typename std::enable_if< std::is_enum<T>::value, bool >::type
func( T &t, int x )
{
// ...
}
答案 2 :(得分:2)
您看到的错误与自动类型扣除有关,而不是enable_if
和is_enum
。
以下作品。
#include <iostream>
#include <type_traits>
template<typename T>
bool func( typename std::enable_if< std::is_enum<T>::value, T >::type &t, int x )
{
return true;
}
enum class Bar { a,b,c };
int main()
{
Bar bar{Bar::a};
std::cout << func<decltype(bar)>(bar, 1) << std::endl;
}
<强>更新强>
正如OP已经发现的那样,使用包装函数可以避免使用decltype
。
#include <iostream>
#include <type_traits>
template <typename T>
bool func( typename std::enable_if< std::is_enum<T>::value, T >::type &t, int x )
{
return true;
}
template <typename T>
bool func2( T &t, int x )
{
return func<T>(t,x);
}
enum class Bar { a,b,c };
int main()
{
Bar bar{Bar::a};
std::cout << func2(bar, 1) << std::endl;
}