class 模板部分特化和 class 成员特化有些令人困惑

something confusing about class template partial specialization & class member specialization

这里我定义了一个 class 模板 Foo,特化了它的成员函数,然后为它提供了一个偏特化 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";
    }
};

然后我在 VS2015 上实例化 Foo,如下所示:

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

令人惊讶的是,f() 打印 "Foo_0",这意味着未选择部分专用模板 Foo_1。 更奇怪的是,当我评论 Foo::bar(double) 的特化时,f() 打印 "Foo_1"!

然后我测试这个:

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

这一次,f() 还打印 "Foo_1",应用了 class 特化。

因此,成员 bar() 的特化似乎确实影响了 class 模板的部分特化的应用。真的吗?为什么会这样?

Foo<double, int>::bar

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

导致 Foo<double, int> 的隐式实例化。这是比部分特化 Foo<T, int> 更好的匹配,所以你得到 Foo_0 而不是 Foo_1,除非你注释掉 bar.

的特化

你可以做的是将 bar(double) 作为常规重载成员函数移入通用 class 模板 Foo<T, S>

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

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

现在您将获得Foo_1live example。请注意,您将无法再调用 Foo<double, int>::bar(double)。如果你想要那样,那么你还需要向偏特化 Foo<T, int> 添加一个 bar 成员。