Search code examples
c++templatesexplicit-specialization

explicit specialization of a function template (which is a member of a class template) produces "partial specialization is not allowed" error, why?


I am working on Visual Studio 2015 community edition

let's say I have, a simple class like this:
(The example below "should be" a compilable because it include all the necessary stuff, unfortunately, it produces an error).

#include <stdexcept>

template <typename T>
class class_foo
{
// members, methods, constructors. not important stuff...

// helper functions:
private:
    class tag_aaa {}; // to resolve few things at compile-time, not run-time.
    class tag_bbb {}; // - || -

    template <typename tag>
    void erase();

    // for some reason this is not interpreted as an error by my compiler:
    template<>
    void erase<tag_aaa>();

    template<>
    void erase<tag_bbb>();
};

// catch-all-do-nothing "version"
// well, catch-all-throw-an-exception because call to this function is an obvious error.
// that should never occur.
template <typename T>
template <typename tag> inline
void class_foo<T>::erase()
{
    throw std::runtime_error("Very weird error...");
}

template <>
template <typename T> inline
void class_foo<T>::erase<class_foo<T>::tag_aaa>()
{
    // do some stuff... 
}

template <>
template <typename T> inline
void class_foo<T>::erase<class_foo<T>::tag_bbb>()
{
    // do some stuff... 
}

int main()
{
    class_foo<double> bar;

    return 0;
}

The error:

1>D:/develop/workspace/visual_studio/nevada_test_site/source/workspace/nevada_test_site/start.cu(36): error : partial specialization of class "class_foo<T>::erase<class_foo<T>::tag_aaa> [with T=T]" is not allowed

1>D:/develop/workspace/visual_studio/nevada_test_site/source/workspace/nevada_test_site/start.cu(43): error : partial specialization of class "class_foo<T>::erase<class_foo<T>::tag_bbb> [with T=T]" is not allowed

1>D:/develop/workspace/visual_studio/nevada_test_site/source/workspace/nevada_test_site/start.cu(51): warning : variable "bar" was declared but never referenced

I think about myself as a junior-hobbyist programmer, so certainly I am wrong, but I believe that both erase<class_foo<T>::tag_aaa>() and erase<class_foo<T>::tag_bbb>() are explicit specializations of the template <typename tag> void erase(); function. And as such, they are allowed. I believe that this error is due to some bad syntax but I can't find an error.

Question:

  • Is what I am trying to do, allowed?
  • If yes, what am I doing wrong?
  • If yes, what is the correct syntax for specializing this functions (erase)?

Solution

  • It look like full specialization of a template function but it's still partial specialization, hence the compilation error.

    Why is it? Well, look at this specialization:

    template <>
    template <typename T>
    inline void class_foo<T>::erase<class_foo<T>::tag_bbb>() {
        // do some stuff...
    }
    

    You said it's a explicit specialization, but there is still a template parameter to fill! There's the parameter T yet to be known. So a specialization... that is still a template? That's a partial specialization!

    Partial specialization of function is not allowed, for many reason. One of them is that it won't play nicely with overloading.

    To effectively specialize the function, you must leave no template parameter to be known, something like this:

    template<>
    template<>
    inline void class_foo<int>::erase<class_foo<int>::tag_bbb>() {
        // do some stuff...
    }
    

    But it's not what you want.


    Here's how I'd fix this problem. Use overloading instead of specializing:

    template<typename T>
    struct class_foo {
    
    private:
        struct tag_aaa {};
        struct tag_bbb {};
    
        void erase(tag_aaa) {
            // Stuff when tag_aaa
        }
    
        void erase(tag_bbb) {
            // Stuff when tag_bbb
        }
    };
    

    Instead of invoking those like this:

    erase<tag_aaa>(); // with specialization
    

    You must invoke it like that:

    erase(tag_aaa{}); // with overloading