简体   繁体   中英

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.)

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.

You can get the effect for which you're looking through the following (which prints out 0 1, BTW):

#include <type_traits>
#include <iostream>

namespace detail
{   
    struct SZugBase{};
}   

template <typename tTYPE>
struct SFoo                                                                                                                                
{   
    struct SZug : public detail::SZugBase {}; 
};  

template<typename tType, bool IsFoo>
struct SBarBase
{   
    int value = 0;
};  

template<typename tType>
struct SBarBase<tType, true>
{   
    int value = 1;
};  

template <typename tTYPE>
struct SBar : public SBarBase<tTYPE, std::is_convertible<tTYPE, detail::SZugBase>::value>
{ /* stuff */ };

int main()
{   
    SBar<int> b0; 
    SBar<SFoo<int>::SZug> b1; 

    std::cout << b0.value << " " << b1.value << std::endl;
}   

Explanation

First, we give SZug a regular-class base:

namespace detail
{   
    struct SZugBase{};
}   

template <typename tTYPE>
struct SFoo                                               
{   
    struct SZug : public detail::SZugBase {}; 
};  

Note the following:

  1. SZugBase is not parameterized by anything, so it is easy to refer to it independently of the parameter of SFoo

  2. SZugBase is in a detail namespace, so, by common C++ conventions, you're telling clients of your code to ignore it.

Now we give SBar two base classes, specialized on whether something is convertible to the non-template base of SZug :

template<typename tType, bool IsFoo>
struct SBarBase
{   
    int value = 0;
};  

template<typename tType>
struct SBarBase<tType, true>
{   
    int value = 1;
};  

Finally, we just need to make SBar a subclass of these bases (depending on the specialization):

template <typename tTYPE>
struct SBar : public SBarBase<tTYPE, std::is_convertible<tTYPE, detail::SZugBase>::value>
{ /* stuff */ };

Note that you don't specialize SBar here, you rather specialize the base classes. This effectively gives the same effect, though.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM