Search code examples
c++visual-studiotemplatesvariadic-templatestemplate-templates

<variadic template> template parameters with default value compilation error in Visual Studio


Faced this obscure problem when porting code from GCC to MSVC.

Consider the following snippet:

template <typename T>
struct Foo;

template <template <typename...> typename Container, typename Arg>
struct Foo<Container<Arg>> {
    using arg_t = Arg;
};

template <typename X>
struct A {};

template <typename X, typename Y = void>
struct B {};

template <typename X, typename Y = void, typename Z = void>
struct C {};

int main() {
    typename Foo<A<int>>::arg_t a;
    typename Foo<B<int>>::arg_t b;
    typename Foo<C<int>>::arg_t c;
    return 0;
}

We use Foo trait for extracting the first argument of a template class, where starting from second template parameters have default values (real use case is with std::unique_ptr for example). Clang and GCC handles this snippet perfectly, however MSVC (the one that comes with Visual Studio 17) throws very unobvious compilation error.


Solution

  • Turned out, GCC and Clang somehow handles default template parameters, so that A<X, Y=void> is accepted by <template <typename...> typename Bar, typename X> Bar<X> interface. On the other hand, MSVC does not. Not sure if it is in standard or just GCC/Clang extension. Anyway, the solution is to add dummy variadic parameter to match leftover parameters.

    template <typename T>
    struct Foo;
    
    template <template <typename...> typename Container, 
              typename Arg, typename... MsvcWorkaround>
    struct Foo<Container<Arg, MsvcWorkaround....>> {
        using arg_t = Arg;
    };
    
    template <typename X>
    struct A {};
    
    template <typename X, typename Y = void>
    struct B {};
    
    template <typename X, typename Y = void, typename Z = void>
    struct C {};
    
    int main() {
        typename Foo<A<int>>::arg_t a;
        typename Foo<B<int>>::arg_t b;
        typename Foo<C<int>>::arg_t c;
        return 0;
    }
    

    It was really hard it understand the problem from the compiler error and I couldn't google out a solution, that's why I want to share mine.