Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Template template partial specialization only working with -std=c++1z with g++

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?

like image 829
Svalorzen Avatar asked Jun 23 '17 17:06

Svalorzen


1 Answers

This is a result of the adoption of P0522 in C++17, whose motivation was, from the example:

template <template <int> class> void FI();
template <template <auto> class> void FA();
template <auto> struct SA { /* ... */ };
template <int> struct SI { /* ... */ };
FI<SA>();  // OK; error before this paper
FA<SI>();  // error

template <template <typename> class> void FD();
template <typename, typename = int> struct SD { /* ... */ };
FD<SD>();  // OK; error before this paper (CWG 150)

Previously, the wording in [temp.arg.template] required template template parameters to match in kind exactly. So given:

template <template <class > class P> class X;

template <class T> class A;
template <class T, class U=T> class B;
template <class... > class C;

X<A> is clearly okay, but X<B> is ill-formed because B takes two template parameters (regardless if one is defaulted!) and X<C> is ill-formed because P expects one template parameter and C takes a pack (even if you could form a C 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> and X<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.

like image 170
Barry Avatar answered Nov 07 '22 14:11

Barry