Search code examples
c++forward-declarationambiguityenable-if

Forward declaring a function that uses enable_if : ambiguous call


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)?


Solution

  • 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 ints, because they both are valid. However, you can disable the first one for ints 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)       
    {       
    }