3
votes

Here I define a class template Foo, specialize its member function, and then supply a partial specialization for that class:

// this is Foo_0
template<typename T, typename S>
class Foo {
public:
    void operator()() {
        std::cout << "Foo_0\n";
    }
    template<typename R>
    void bar(R) {
        std::cout << "I'm from Foo_0\n";
    }
};
template<>
template<>
void Foo<double, int>::bar(double) {
    std::cout << "Now I'm specialized!\n";
}

// this is Foo_1
template<typename T>
class Foo<T, int> {
public:
    void operator()() {
        std::cout << "Foo_1\n";
    }
};

And I instantiate Foo on VS2015 like this:

Foo<double, int> f;
f();

Surprisingly, f() prints "Foo_0", which means the partial specialized template Foo_1 isn't chosen. What's even stranger, when I comment the specialization of Foo::bar(double), f() prints "Foo_1"!

Then I test this:

Foo<int, int> f;
f();

This time, f() also prints "Foo_1", the class specialization is applied.

So it seems that the specialization of member bar() do influence the application of partial specialization of the class template. Is that true? Why does it work like this?

1
Try moving the explicit function template specialization below the partial class template specialization.Kerrek SB
@Kerrek SB When I move it, the compiler complains that "class Foo doesn't have member Bar", it seems that bar(double) is considered as a member of Foo_1.Dardai
gcc succeeds to diagnose the problem (contrary to clang) Demo. (error: partial specialization of 'class Foo<T, int>' after instantiation of 'class Foo<double, int>' [-fpermissive])Jarod42
The explicit specialization of bar cause an implicit instantiation of Foo<double, int>, which is a better match than Foo<T, int> so you get Foo_0 with the specialized bar and Foo_1 when you comment it out.TemplateRex

1 Answers

3
votes

The explicit specialization of Foo<double, int>::bar

template<>
template<>
void Foo<double, int>::bar(double) {
    std::cout << "Now I'm specialized!\n";
}

causes an implicit instantiation of Foo<double, int>. This is a better match than the partially specialized Foo<T, int>, so you get Foo_0 instead of Foo_1, unless you comment out the specialization of bar.

What you could do is to move bar(double) into the general class template Foo<T, S> as a regular overloaded member function

template<class T, class S>
class Foo {
    // as before

    void bar(double) {
        std::cout << "Now I'm overloaded!\n";
    }
};

Now you will get Foo_1, live example. Note that you won't be able to call Foo<double, int>::bar(double) anymore. If you want that, then you need to add a bar member to the partial specialization Foo<T, int> as well.