Search code examples
c++c++11enable-ifclass-members

enable_if class member function with separate definition


I'm using enable_if class member functions to iterate over a variational template argument. Here is a minimal example (without the actual variadic)

#include <iostream>

template<int size> class Test {
    public:
        template<int i = 0> typename std::enable_if<i == size, void>::type test() {}

        template<int i = 0> typename std::enable_if<i < size, void>::type test() {
            std::cout << "cycle: " << i << '\n';
            test<i + 1>();
        }
};

int main(int, char**) {
    Test<10> a;
    a.test<>();
}

It works just fine but now I'm having problems with dependencies and decided to separate the declarations and definitions. I tried this:

#include <iostream>

template<int size> class Test {
    public:
        template<int i = 0> void test();
};

template<int size>
template<int i> typename std::enable_if<i == size, void>::type Test<size>::test() {}

template<int size>
template<int i> typename std::enable_if<(i < size), void>::type Test<size>::test() {
    std::cout << "cycle: " << i << '\n';
    test<i + 1>();
}

int main(int, char**) {
    Test<10> a;
    a.test<>();
}

but GCC says that error: out-of-line definition of 'test' does not match any declaration in 'Test<size>'. I managed to make it work by including definitions for both cases of test. My question is: why doesn't this work? Shouldn't the compiler only find one of the declarations for any i? Thank you in advance for any help!


Solution

  • template<int i = 0> 
    typename std::enable_if<i == size, void>::type test() { }
    
    template<int i = 0> 
    typename std::enable_if<i < size, void>::type test() { /* ... */ }
    

    The two member functions above are completely different, they just happen to have the same name test. They have different signatures and must be declared separately. It's similar to writing:

    template<int i = 0> 
    int test() { }
    
    template<int i = 0> 
    float test() { /* ... */ }
    

    Would you expect to be able to have a single declaration for both of those in your class definition?