Question

I am basically trying to do the same as std::enable_if : parameter vs template parameter but I can't get my code to compile.

I had a simple first version that has the std::enable_if in the parameters and which works fine:

#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;
}

But I thought it might be more concise if the template stuff was in one place and wanted the enable_if out of the function arguments.

Now if I simply move the enable_if part I get the following:

#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;
}

The #pragma warning(1 : 4519) I needed because otherwise default arguments on a function template are an error in VS2010. The problem is it still doesn't compile. The first message was: error C2783: 'void foo(T)' : could not deduce template argument for '__formal' and even though I don't want to do that I tried explicitly stating which template I want by calling it using

  foo<int, int>(10);
  foo<double, double>(10.1);

but it still doesn't work and the new error is.

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

I hope somebody can tell me how to fix this and of course all comments on my style and other issues my code might have are welcome. :)

Extra question: Does anybody know why VS2010 does not allow default arguments on function templates by default?

Était-ce utile?

La solution

The problem is that the second template parameter in std::enable_if defaults to void. So your doing something which is pretty much the same as:

template <typename T, void = 0>

Which makes substitution fail always. You could use a non-type template argument of type int, which you can give a default 0 value:

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

Do the same for both overloads, and it will work.

Demo here.

Autres conseils

A default = 0 makes no sense for types. Instead, you want a default type:

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

Now foo participates in overload resolution only if the default type for the second argument exists, i.e. only if the condition is true.

Defaultable template arguments for function templates were newly introduced in C++11, by the way. (They had simply been forgotten previously.)

Note that this gets cumbersome if you want multiple overloads, since you mustn't have repeated, identical template signatures. You could give each overload a distinct set of dummy template arguments, but that does not scale well. This post illustrates a better solution using variadic template packs (also available here).

Instead of enable_if, in this situation, you could just do plain overloading:

#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;
}

An exact match is preferred which makes the compiler select the template in case the argument is not an int. In case you provide an int, we have two exact matches and the non-template is preferred. If you like to further constrain the template in a C++11 way, you can write:

#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;
}

which kicks the template out of the overload resolution set before overload resolution is done. But as I said, in case T=int, the non-template would win anyways.

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top