Search code examples
c++c++14

Partial template specialization match differs between VS2019 and VS2022 when compiling


Following code is a runnable sample which is a simplification of our production code.
When compiling with VS 2019 (Platform toolset 142) the output is 2.
However, when compiling in VS 2022 (Platform toolset 143) the output is 1.

Why is that? and how can we make sure the desired (same for both toolsets) partial specialization is matched?

Example.cpp

#include <iostream>

template <typename T>
class TestB {
public:
    using Type = T;
};

template <typename T1, typename T2, typename ... PP>
class TestA
{
public:
    static void print() { std::cout << "1\n"; };
};

template <typename T1, typename T2, typename P1, typename ... PP>
class TestA<T1, T2, P1, PP...> : public TestA<T1, typename P1::Type, PP...>
{
};

template <typename T1, typename ... PP>
class TestA<T1, T1, PP...>
{
public:
    static void print() { std::cout << "2\n"; };
};

int main(int, char* [])
{
    TestA<int, int, TestB<int>, TestB<double>>::print();
    return 0;
}

Expected is that when template param 1 and 2 are equal, it should match the final specialization (represented by print 2).
In reality in production code we map up certain parameters of the object when we hit this spec.


Solution

  • The question might have been poorly phrased since it was a bit tricky to create a simple test case the resembled the actual code.

    However, a colleague found a solution that seem to work as expected. Adding a metafunction that checks if T1 and T2 are the same allows the compiler to select the correct specialization.

    #include <iostream>
    
    template <typename T>
    class TestB {
    public:
        using Type = T;
    };
    
    template <typename ... PP>
    class TestA;
    
    template <typename T1, typename T2, typename P1, typename ... PP>
    class TestA<T1, T2, typename std::enable_if_t<!std::is_same_v<T1, T2>>, P1, PP...> : public TestA<T1, typename P1::Type, void, PP...>
    {
    
    };
    
    template <typename T1, typename T2, typename ... PP>
    class TestA<T1, T2, typename std::enable_if_t<std::is_same_v<T1, T2>>, PP...>
    {
    public:
        void print() const { std::cout << typeid(*this).name() << std::endl; };
    };
    
    int main(int, char* [])
    {
        TestA<int, double, void, TestB<int>, TestB<double>>().print();
        TestA<int, int, void, TestB<int>, TestB<double>>().print();
        return 0;
    }
    

    Tank you all for your effort to help.