Search code examples
c++templatespartial-specialization

C++: Partially specializing template's type parameter as another template class's member-type


I have a template struct SFoo that contains a member struct SZug:

template <typename tTYPE>
struct SFoo
  {
    struct SZug {};
  };

I have another struct SBar that takes a type parameter:

template <typename tTYPE>
struct SBar
  { /* stuff */ };

I would like to specialize SBar using SZug for the type parameter, like so:

template <typename tTYPE>
struct SBar<typename SFoo<tTYPE>::SZug>
  { /* different stuff */ };

This doesn't compile - LLVM outputs:

non-deducible template parameter 'tTYPE'

While a compiler could easily deduce this if it wished, I'm guessing it's just that the C++ spec would need to specifically cover this case.

Is there any way to achieve this?
(note: I'm currently working around it by moving SZug outside of SFoo and using a using declaration, but it's ugly.)


Solution

  • I am not sure I fully understood what you want to do, but you could try the following (it only requires adding a specific attributes to SZug:

    template <typename tTYPE>
    struct SFoo {
        struct SZug {
            // Add this to be able to obtain SFoo<T> from SFoo<T>::SZug
            using type = tTYPE;
        };
    };
    

    Then a small template to check if a type is a SFoo<T>::SZug:

    template <typename tTYPE, typename Enabler = void>
    struct is_SZug: public std::false_type { };
    
    template <typename tTYPE>
    struct is_SZug<tTYPE, typename std::enable_if<
      std::is_same<tTYPE, typename SFoo<typename tTYPE::type>::SZug>{}
    >::type>: public std::true_type { };
    

    And a slight modification to the SBar template to enable the "specialization" if the type is a SZug:

    template <typename tTYPE, typename Enabler = void>
    struct SBar
      { static void g(); };
    
    template <typename tTYPE>
    struct SBar<tTYPE, typename std::enable_if<is_SZug<tTYPE>{}>::type>
      { static void f(); };
    

    A little check:

    void f () {
      SBar<int>::g();
      SBar<SFoo<int>::SZug>::f();
    }
    

    Note: You could also directly set SFoo<T> as the type attribute in SFoo<T>::SZug, you would simply need to change the second argument of std::is_same a little.