views:

291

answers:

3

I have some trouble forward declaring a function that uses boost::enable_if: the following piece of code gives me a compiler error:

// Declaration
template <typename T>
void foo(T t);

// Definition
template <typename T>
typename boost::enable_if<boost::is_same<T, int> >::type foo(T t)
{
}

int main()
{
    foo(12);
    return 0;
}

When compiling, I get an "ambiguous call to foo" error. According to the definition of enable_if, the 'type' typedef corresponds to void when the condition is true, so as far as I can see, the two signatures of foo match. Why does the compiler think they are different, and is there a correct way to forward declare foo (preferably without repeating the enable_if part)?

A: 

should you be using boost::enable_if_c instead of boost::enable_if?

DarenW
I'm not totally clear on understanding the fancier boost template stuff - i'm hoping to learn something even if this is a bad answer!
DarenW
This is not exactly "answer material". And no, `enable_if` is fine because the boolean constant is wrapped inside a type (`is_same<>`)
sellibitze
`enable_if<Predicat>::type` is the same as `enable_if_c<Predicat::value>::type`. Since I use a metafunction (`is_same`) which result is accessed through `::value`, there is no problem in this regard.
Luc Touraille
+1  A: 

The problem is that the declaration and the definition do not match.

The solution is that the declaration should contain the exact same signature, and the the enable_if bit.

#include <boost/type_traits/is_same.hpp>
#include <boost/utility/enable_if.hpp>

// Declaration
template <typename T>
typename boost::enable_if<boost::is_same<T, int> >::type foo(T t);

// Definition
template <typename T>
typename boost::enable_if<boost::is_same<T, int> >::type foo(T t)
{
}

int main()
{
    foo(12);
    return 0;
}

This compiles fine on VC2008.

Matthieu M.
+1  A: 

This is not only a problem with enable_if. You get the same error on Visual Studio and gcc with the following code:

struct TypeVoid {
  typedef void type;
};

template<typename T>
void f();

template<typename T>
typename T::type f() {
}

int main()
{
  f<TypeVoid>();
  return 0;
}

I think the main problem is that the return type (before instantiation) is part of the signature of a template function. There is more information here.

Regarding your code, if the declaration refers to the definition, you should match both:

// Declaration       
template <typename T>       
typename boost::enable_if<boost::is_same<T, int> >::type foo(T t);       

// Definition       
template <typename T>       
typename boost::enable_if<boost::is_same<T, int> >::type foo(T t)       
{       
}

If the declaration refers to a different function, the compiler would never be able to choose the correct one for *int*s, because they both are valid. However, you can disable the first one for *int*s using *disable_if*:

// Other function declaration
template <typename T>
typename boost::disable_if<boost::is_same<T, int> >::type foo(T t);

// Defition
template <typename T>       
typename boost::enable_if<boost::is_same<T, int> >::type foo(T t)       
{       
}
J. Calleja