Ошибка специализации шаблона для шаблонной структуры внутри шаблонной структуры

Кажется, я не могу понять, что происходит не так со следующим примером.

$ cat repro.cpp
#include <iostream>
using namespace std;

template<class T>
struct S1_t
{
  template<class U>
  struct inner_t_x {};

  using inner_t = inner_t_x<T>;
};
struct Bogus{};

template<class T>
struct Functor1
{
  void operator()() 
  {
    cout << "Functor1 FAIL: " << __PRETTY_FUNCTION__ << endl;
  }
};
template<>
struct Functor1<typename S1_t<Bogus>::template inner_t_x<Bogus>>
{
  void operator()() 
  {
    cout << "Functor1 PASS: " << __PRETTY_FUNCTION__ << endl;
  }
};

template<class T>
struct Functor2
{
  void operator()() 
  {
    cout << "Functor2 FAIL: " << __PRETTY_FUNCTION__ << endl;
  }
};
template<class T>
struct Functor2<typename S1_t<T>::template inner_t_x<T>>
{
  void operator()() 
  {
    cout << "Functor2 PASS: " << __PRETTY_FUNCTION__ << endl;
  }
};

template<class T>
void eval()
{
  Functor1<T>{}();
  Functor2<T>{}();
}

int main()
{
  eval<S1_t<Bogus>::inner_t>();
  return 0;
}

$ clang++ repro.cpp -std=c++11 -Wall && ./a.out
Functor1 PASS: void Functor1<S1_t<Bogus>::inner_t_x<Bogus> >::operator()()
Functor2 FAIL: void Functor2<S1_t<Bogus>::inner_t_x<Bogus> >::operator()() [T = S1_t<Bogus>::inner_t_x<Bogus>]

В Functor1 я явно специализируюсь на типе Bogus, и эта специализация действительно вызывается. Однако в Functor2 допускается вывод типа, но частичная специализация терпит неудачу, и вместо этого создается экземпляр общего шаблона. Тем не менее, __PRETTY_PRINT__ показывает одну и ту же подпись для Functor1 и Functort2 при создании экземпляра.

Может ли кто-нибудь объяснить это поведение, и есть ли способ исправить частичную специализацию Functor2, чтобы соответствовать этому сценарию? Спасибо!

fwiw: Изменение частичной специализации Functor2 на

template<class T>
struct Functor2<typename S1_t<Bogus>::template inner_t_x<T>>
{
  void operator()() 
  {
    cout << "Functor2 PASS: " << __PRETTY_FUNCTION__ << endl;
  }
};

дает правильный вывод

Functor1 PASS: void Functor1<S1_t<Bogus>::inner_t_x<Bogus> >::operator()()
Functor2 PASS: void Functor2<S1_t<Bogus>::inner_t_x<Bogus> >::operator()() [T = S1_t<Bogus>::inner_t_x<Bogus>]

но это не вариант в моем случае использования.


person eg0x20    schedule 14.12.2015    source источник
comment
Почему в template<> struct Functor1 нет типа? Может быть, это просто какой-то особый синтаксис, которого я не знаю, но выглядит это странно.   -  person maddin45    schedule 14.12.2015


Ответы (2)


Специализация Functor2 никогда не будет использоваться. Компилятор может выдать предупреждение об этом, но не в вашем случае. Причина того, что это невозможно вывести, проста: представьте, что позже вы добавите что-то вроде

struct Hogus;

template<>
struct S1_t<Hogus>
{
  template <typename U>
  using inner_t_x = S1_t<Bogus>::inner_t_x<Bogus>;
};

тогда S1_t<Bogus>::inner_t_x<Bogus> и S1_t<Hogus>::inner_t_x<Hogus> будут одного типа. Следовательно, вывод шаблона в частичной специализации Functor2 может дать как T=Bogus, так и T=Hogus. Следовательно, ни при каких обстоятельствах нельзя вывести его однозначно.

person Ralph Tandetzky    schedule 14.12.2015
comment
Спасибо за наглядный пример, вдруг очень понятно. Есть ли способ однозначно вывести тип T? Я чувствую, что их нет, но я не уверен на 100%. - person eg0x20; 14.12.2015
comment
Я не думаю, что есть способ вывести внешнюю и внутреннюю часть вложенного типа. Извиняюсь. - person Ralph Tandetzky; 14.12.2015

typename S1_t<T>::..не выводится в частичной специализации.

Вы получили более четкую ошибку при использовании inner_t вместо inner_t_x<T>:

main.cpp:41:8: error: template parameters not deducible in partial specialization:

 struct Functor2<typename S1_t<T>::inner_t>

        ^

main.cpp:41:8: note:         'T'

main.cpp:41:8: warning: class template partial specialization contains a template parameter that cannot be deduced; this partial specialization will never be used
person Jarod42    schedule 14.12.2015
comment
Ссылки (относительно n4567): [temp.deduct.type]p5 и p6 Когда имя типа указано таким образом, что включает невыведенный контекст, все типы, составляющие это имя типа, также выведено. - person dyp; 14.12.2015
comment
Другими словами, абсолютно невозможно вывести T. Это правильно? Если это так, я был введен в заблуждение отсутствием предупреждения компилятора, думая, что вывод типа был сделан для ::inner_t_x. - person eg0x20; 14.12.2015