Search code examples
c++templatesc++11partial-specialization

Template specialisation failure for a templated structure inside a templated structure


I can't seem to wrap my head around what is going wrong with the following example

$ 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>]

In the Functor1, I explicitly specialise on the type Bogus, and indeed that specialisation is being invoked. However, in the Functor2 the type is allowed to be deduced, but partial specialisation fails and generic template is instantiated instead. Yet, __PRETTY_PRINT__ show the same signature for Functor1 & Functort2 when instantiated.

Can anyone able to explain this behaviour, and is there a way to fix Functor2 partial specialisation to fit this scenario? Thanks!

fwiw: Changing Functor2 partial specialisation to

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

gives correct output

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

but this is not the option in my use case.


Solution

  • The specialization of Functor2 will never be used. The compiler could issue a warning about that, but it doesn't in your case. The reason for it being not deducible is simple: Imagine you later add something like

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

    then S1_t<Bogus>::inner_t_x<Bogus> and S1_t<Hogus>::inner_t_x<Hogus> would be the same type. Therefore, the template deduction in the partial specialization of Functor2 could yield both T=Bogus or T=Hogus. Hence, it cannot be deduced unambiguously under any circumstances.