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.
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.