5

我基本上试图做与std::enable_if 相同的事情:参数与模板参数,但我无法编译我的代码。

我有一个简单的第一个版本,它的参数中有 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 警告(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默认不允许函数模板上的默认参数吗?

4

3 回答 3

11

问题是第二个模板参数std::enable_if默认为void. 因此,您所做的事情与以下内容几乎相同:

template <typename T, void = 0>

这使得替换总是失败。你可以使用 type 的非类型模板参数int,你可以给它一个默认的 0 值:

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

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

演示在这里

于 2012-08-27T12:14:11.453 回答
5

默认值对类型= 0没有意义。相反,您需要一个默认类型

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

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

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

请注意,如果您想要多个重载,这会变得很麻烦,因为您不能有重复的、相同的模板签名。您可以为每个重载提供一组不同的虚拟模板参数,但这不能很好地扩展。这篇文章说明了使用可变参数模板包的更好解决方案(也可在此处获得)。

于 2012-08-27T12:10:46.633 回答
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,非模板无论如何都会赢。

于 2012-08-27T12:46:40.253 回答