为什么带有通用引用的过载(使用enable_if)不起作用?

时间:2013-02-24 05:15:14

标签: c++ c++11 overloading template-meta-programming

使用MS Visual C ++ 2012版本11.0.51106.01更新1:

int x()
{
   return 3;
}

template <typename Fun>
typename std::enable_if<std::is_same<Fun, int()>::value, int>::type 
MySh(Fun& f)
{ 
   return f();
}

template <typename Fun>
typename std::enable_if<std::is_same<Fun, int()>::value, int>::type 
MySh1(Fun&& f)
{ 
   return f();
}

template <typename Fun>
int MySh2(Fun&& f)
{ 
   return f();
}

主叫代码:

   int res = MySh(x); // compiles and returns 3
   res = MySh1(x); // doesn't compile with error: error C2893: Failed to specialize function template 'std::enable_if<std::is_same<Fun,int(void)>::value,int>::type MySh1(Fun &&)
   res = MySh2(x); // compiles and returns 3

我还没有尝试过其他编译器(但是)的目的是让它与Visual C ++ 2012一起使用和/或向Microsoft报告编译器错误。

我想确保我不会忽视一些微不足道的事情并犯下一个愚蠢的错误。 当然,样本只是一个提取,真正意图的用例更复杂,并与以下内容相关: Overloading on callables question

修改 我还对其他考虑因素感到困惑,例如,对于ex:

   std::is_same<decltype(x), int()>::value; // true
   std::is_same<decltype(x), int(&)()>::value; //false

template <typename Fun>
typename std::enable_if<std::is_same<Fun, int(&)()>::value, int>::type 
MySh1(Fun&& f)
{ 
   std::cout << typeid(f).name() << std::endl;   // prints int __cdecl(void)
   return f();
}

显然,我没有注意参数类型和参数类型之间的区别(Fun与f和x相对)。

2 个答案:

答案 0 :(得分:2)

答案在错误中,用

替换你的声明
template <typename Fun>
typename std::enable_if<std::is_same<Fun, int(&)()>::value, int>::type 
    MySh1(Fun&& f)
{ 
    return f();
}

这是因为模板<typename T> void foo(T&&);

的案例具有特殊处理标准

如果您将一些参数传递给foo,则以下内容成立(对于int作为示例):

  • 通过lvalue int - Tint&
  • 通过lvalue const int - Tconst int&
  • 通过rvalue int - Tint
  • 通过rvalue const int - Tconst int

Scott Meyers的好article可能会对它有更多的启发。

答案 1 :(得分:2)

该上下文中的FunFun&Fun const&Fun&& - &&使Fun绑定到任何以上3个引用中的一个(也许Fun const&&?不确定)。 &&表示类型推导上下文中的魔术引用。

您将其与int()进行了比较,而不是将其与参考或const引用或对它的rvalue引用进行比较。

如果您不关心std::decaycv附加的引用类型,我建议您使用Fun

template <typename Fun>
typename std::enable_if<std::is_same<typename std::decay<Fun>::type, int(*)()>::value, int>::type 

更好的选择可能是is_convertible

template <typename Fun>
typename std::enable_if<std::is_convertible<Fun, int(*)()>::value, int>::type 

允许[]()->int lambdas有资格成为可用的Fun。但为什么要止步呢?

template <typename Fun>
typename std::enable_if<std::is_convertible<decltype(std::declval<Fun>()()), int>::value>::type 

接受任何类型Fun,以便应用Fun的{​​{1}}实例返回可转换为operator()的类型。

您甚至可以进一步了解其中一项:

int

是等效的,并且表示template <typename Fun> auto MySh_Alpha(Fun&& f)->decltype( f() ) { return f(); } template <typename Fun> decltype( std::declval<Fun>()() ) MySh_Alpha2(Fun&& f) { return f(); } 返回与传入的MySh_Alpha相同的类型。