I have found that the following piece of code:
#include <iostream>
#include <vector>
template <typename T>
struct X : std::false_type {};
template <template <typename> class Y, typename U>
struct X<Y<U>> : std::true_type {};
int main() {
if (X<int>())
std::cout << "wrong\n";
if (X<std::vector<double>>())
std::cout << "correct\n";
return 0;
}
Only prints correct
when compiled with g++-7
with -std=c++1z
. Other versions of g++
, clang++
or other std
flags fail to produce correct.
Is this a bug of the current implementation, and this code should not print anything, or is something changed in C++17 which makes this code work as I expect?
This is a result of the adoption of P0522 in C++17, whose motivation was, from the example:
Previously, the wording in [temp.arg.template] required template template parameters to match in kind exactly. So given:
X<A>
is clearly okay, butX<B>
is ill-formed becauseB
takes two template parameters (regardless if one is defaulted!) andX<C>
is ill-formed becauseP
expects one template parameter andC
takes a pack (even if you could form aC
with only a single parameter!)The new wording loosens the idea of a match to one which makes more sense - if the template template-parameter is at least as specialized as the argument. That makes
X<B>
andX<C>
both work.Hence, in C++17,
X<std::vector<double>>
should pick the specialization. But before C++17, it should pick the primary. gcc is doing the right thing.