Invalid use of incomplete type for partial template specialization c++

2k views Asked by At

I am trying to specialize a class method foo(). This works well for full template specialization. However, this does not work with partial template specialization.

Here the example code which compiles fine on GCC and Clang :

#include <iostream>
#include <string>

template <typename Key, typename Value>
struct SimpleKey {
    Key   key;
    Value value;
    void foo() const { 
        std::cout << "base" << std::endl; 
    }
};

/*
// Uncomment this and it won't work !
template<typename Key>
void SimpleKey<Key, std::string>::foo() const {
    std::cout << "partial" << std::endl; 
}
*/

template<>
void SimpleKey<int, std::string>::foo() const {
    std::cout << "full" << std::endl; 
}


int main() {
    SimpleKey<double, std::string> key1{1.0,"key1"};
    key1.foo();
    SimpleKey<int, std::string> key2{1,"key2"};
    key2.foo();
}

The error on Clang and GCC I get when uncommenting the relevant code is :

error: invalid use of incomplete type ‘struct SimpleKey >’ void SimpleKey::foo() const {

What should I do to get partial template specialization to work properly with "minimal" efforts ?

2

There are 2 answers

2
T.C. On BEST ANSWER

You can explicitly specialize a member function of a particular implicit instantiation of a class template. But this is not allowed with partial specializations. If you don't want to write a full partial specialization, you can consider using tag dispatch:

private:
void foo(std::true_type /*value_is_string*/) const { /* "partial" */ }
void foo(std::false_type /*value_is_string*/) const { /* "base" */ }

public:
void foo() const { return foo(std::is_same<Value, std::string>()); }

or refactoring foo() into a base class template that you partially specialize.

1
johan d On

It's not possible directly. (It's a shame, this syntax is nice) But you can do something like this:

namespace detail {
    inline void f_(int i) { /* spé for int */}
    inline void f_(long i) { /* spé for long*/}
    /* other spe... */
}

template<class T>
struct Foo{
    void f(T arg) { detail::f_(arg);}
};

It's not as direct, but it's still easily readable.