将std :: enable_if从参数移动到模板参数

时间:2012-08-27 12:07:06

标签: c++ function-templates

我基本上尝试和std::enable_if : parameter vs template parameter做同样的事情,但我无法编译代码。

我有一个简单的第一个版本,在参数中有std :: enable_if,并且工作正常:

#include <iostream>
#include <type_traits>

template <typename T>
void foo(T t, typename std::enable_if< std::is_same<T, int>::value >::type* = 0) {
  std::cout << "int" << std::endl;
}

template <typename T>
void foo(T t, typename std::enable_if< !std::is_same<T, int>::value >::type* = 0) {
  std::cout << "not int" << std::endl;
}

int main(int argc, char* argv[])
{
  foo(10);
  foo(10.1);
  return 0;
}

但是我认为如果模板的东西在一个地方并且希望函数参数中的enable_if可能会更简洁。

现在,如果我只是移动enable_if部分,我会得到以下内容:

#pragma warning(1 : 4519)

#include <iostream>
#include <type_traits>

template <typename T, typename std::enable_if< std::is_same<T, int>::value >::type = 0>
void foo(T t) {
  std::cout << "int" << std::endl;
}

template <typename T, typename std::enable_if< !std::is_same<T, int>::value >::type = 0>
void foo(T t) {
  std::cout << "not int" << std::endl;
}

int main(int argc, char* argv[])
{
  foo(10);
  foo(10.1);
  return 0;
}

#pragma warning(1:4519)我需要,因为否则函数模板上的默认参数是VS2010中的错误。问题是它仍然无法编译。 第一条消息是:error C2783: 'void foo(T)' : could not deduce template argument for '__formal' 即使我不想这样做,我试着通过使用

调用它来明确说明我想要的模板
  foo<int, int>(10);
  foo<double, double>(10.1);

但它仍然不起作用,新错误是。

error C2975: 'foo' : invalid template argument for 'unnamed-parameter', expected compile-time constant expression

我希望有人可以告诉我如何解决这个问题,当然所有关于我的风格和我的代码可能有的其他问题的评论都是受欢迎的。 :)

额外的问题:有人知道为什么VS2010默认情况下不允许在函数模板上使用默认参数吗?

3 个答案:

答案 0 :(得分:11)

问题是std::enable_if中的第二个模板参数默认为void。所以你做的事情与以下几点完全相同:

template <typename T, void = 0>

这使得替换总是失败。您可以使用类型为int的非类型模板参数,您可以为其指定默认值0:

template <typename T, typename std::enable_if< std::is_same<T, int>::value, int >::type = 0>

对两个重载都做同样的事情,它会起作用。

演示here

答案 1 :(得分:5)

默认= 0类型没有意义。相反,您需要默认的类型

template <typename T, typename = typename std::enable_if<cnd>::type>
void foo(T) { /* ... */ }

现在foo仅在第二个参数的默认类型存在时参与重载解析,即仅当条件为真时才会参与。

顺便提一下,在C ++ 11中新引入了函数模板的可缺省模板参数。 (他们以前只是被遗忘了。)

请注意,如果您想要多次重载,这会很麻烦,因为您不能拥有重复的,相同的模板签名。您可以为每个重载提供一组不同的虚拟模板参数,但这不能很好地扩展。 This post说明了使用可变参数模板包(也是available here)的更好解决方案。

答案 2 :(得分:1)

在这种情况下,您可以只进行简单重载,而不是enable_if:

#include <iostream>

void foo(int) {
  std::cout << "int" << std::endl;
}

template <typename T>
void foo(T) {
  std::cout << "not int" << std::endl;
}

int main(int argc, char* argv[])
{
  foo(10);
  foo(10.1);
  return 0;
}

首选完全匹配,这使得编译器在参数不是int时选择模板。如果您提供int,我们有两个完全匹配,非模板是首选。如果您想以C ++ 11方式进一步约束模板,可以编写:

#include <iostream>
#include <type_traits>

#define REQUIRES(...) ,class=typename std::enable_if<(__VA_ARGS__)>::type

void foo(int) {
  std::cout << "int" << std::endl;
}

template <typename T
  REQUIRES(!std::is_same<T,int>::value)
>
void foo(T) {
  std::cout << "not int" << std::endl;
}

int main(int argc, char* argv[])
{
  foo(10);
  foo(10.1);
  return 0;
}

在重载完成之前将模板从重载决策集中踢出。但正如我所说,如果T = int,非模板无论如何都会赢。