Conditional compile-time inclusion/exclusion of code based on template argument(s)?

后端 未结 5 2015
陌清茗
陌清茗 2020-12-08 07:39

Consider the following class, with the inner struct Y being used as a type, eg. in templates, later on:

template
class X{
  templat         


        
相关标签:
5条回答
  • 2020-12-08 08:07

    You can use a meta function (here: inlined boost::mpl::if_c, but could be arbitrarily complex) to select the one you want. You need some scaffolding to be able to use constructors, though:

    template <int I>
    class X {
        template <typename T1>
        class YforIeq1 { /* meat of the class */ };
        template <typename T1, typename T2>
        class YforIeq2 { /* meat of the class */ };
    public:
        template <typename T1, typename T2=boost::none_t/*e.g.*/>
        struct Y : boost::mpl::if_c<I==1,YforIeq1<T1>,YforIeq2<T1,T2> >::type {
            typedef typename mpl::if_c<I==1,YforIeq1<T1>,YforIeq2<T1,T2> >::type YBase;
            /* ctor forwarding: C++0x */
            using YBase::YBase;
            /* ctor forwarding: C++03 (runs into perfect fwd'ing problem)*/
            Y() : YBase() {}
            template <typename A1>
            Y(const A1&a1) : YBase(a1) {}
            template <typename A1, typename A2>
            Y(const A1&a1, const A2&a2) : YBase(a1,a2) {}
            // ...
        };
    };
    

    If there's a problem with both YforIeqN being instantiated for each X, then you can try wrapping them as a nullary meta function (something along the way mpl::apply does) and use mpl::eval_if_c.

    0 讨论(0)
  • 2020-12-08 08:08

    Here you go:

    http://ideone.com/AdEfl

    And the code:

    #include <iostream>
    
    template <int I>
    struct Traits
    {
      struct inner{};
    };
    
    template <>
    struct Traits<1>
    {
      struct inner{
        template<class T1>
        struct impl{
          impl() { std::cout << "impl<T1>" << std::endl; }
        };
      };
    };
    
    template <>
    struct Traits<2>
    {
      struct inner{
        template<class T1, class T2>
        struct impl{
          impl() { std::cout << "impl<T1, T2>" << std::endl; }
        };
      };
    };
    
    template<class T>
    struct Test{};
    
    template<class T, class K>
    struct Foo{};
    
    template<int I>
    struct arg{};
    
    template<
      template<class, class> class T,
      class P1, int I
    >
    struct Test< T<P1, arg<I> > >{
      typedef typename Traits<I>::inner inner;      
    };
    
    template<
      template<class, class> class T,
      class P2, int I
    >
    struct Test< T<arg<I>, P2 > >{
      typedef typename Traits<I>::inner inner;      
    };
    
    // and a bunch of other partial specializations
    
    int main(){
    
      typename Test<Foo<int, arg<1> > >::inner::impl<int> b;
      typename Test<Foo<int, arg<2> > >::inner::impl<int, double> c;
    }
    

    Explanation: Basically it's an extension of the idea of partial specialization, however the difference is that rather than specializing within Test, delegate to a specific class that can be specialized on I alone. That way you only need to define versions of inner for each I once. Then multiple specializations of Test can re-use. The inner holder is used to make the typedef in the Test class easier to handle.

    EDIT: here is a test case that shows what happens if you pass in the wrong number of template arguments: http://ideone.com/QzgNP

    0 讨论(0)
  • 2020-12-08 08:11

    Can you try below (it is not partial specialization):

    template<int I>
    class X
    {
    };
    
    template<>
    class X<1>
    {
      template<class T1>
      struct Y{};
    };
    
    template<>
    class X<2>
    {
      template<class T1, class T2>
      struct Y{};
    };
    

    I doubt if the answer is that simple !!

    Edit (Mocking Partial specialization): @Xeo, I was able to compile following code and seems to be fullfilling.

    template<int I>
    struct X
    {
      struct Unused {};  // this mocking structure will never be used
    
      template<class T1, class T2 = Unused>  // if 2 params passed-->ok; else default='Unused'
      struct Y{};
    
      template<class T1> 
      struct Y<T1, Unused>{}; // This is specialization of above, define it your way
    };
    
    int main()
    {
      X<1>::Y<int> o1;  // Y<T1 = int, T2 = Unused> called
      X<2>::Y<int, float> o2; // Y<T1 = int, T2 = float> called
    }
    

    Here, however you can use X<1>, X<2> interchangeably. But in the broader example you mentioned, that is irrelevant. Still if you need, you can put checks for I = 1 and I = 2.

    0 讨论(0)
  • 2020-12-08 08:17

    How about this approach - http://sergey-miryanov.blogspot.com/2009/03/template-class-overriding.html? (sorry for russian)

    0 讨论(0)
  • 2020-12-08 08:20

    There are two problems here:

    1. enable_if works with partial specialization, not primary templates.
    2. The number of externally-visible arguments is determined by the primary template, of which there may be only one.

    Answer 1.

    As you suggested in chat, a linked list of templates can emulate the variadic parameter pack.

    template<int I>
    class X{
      template<class list, class = void>
      struct Y;
    
      template<class list>
      struct Y< list, typename std::enable_if<I==1>::type > {
          typedef typename list::type t1;
      };
    
      template<class list>
      struct Y< list, typename std::enable_if<I==2>::type > {
          typedef typename list::type t1;
          typedef typename list::next::type t2;
      };
    };
    

    If you end up with next::next::next garbage, it's easy to write a metafunction, or use Boost MPL.


    Answer 2.

    The different-arity templates can be named similarly but still stay distinct if they are nested inside the SFINAE-controlled type.

    template<int I>
    class X{
      template<typename = void, typename = void>
      struct Z;
    
      template<typename v>
      struct Z< v, typename std::enable_if<I==1>::type > {
          template<class T1>
          struct Y{};
      };
    
      template<typename v>
      struct Z< v, typename std::enable_if<I==2>::type > {
          template<class T1, class T2>
          struct Y{};
      };
    };
    
    X<1>::Z<>::Y< int > a;
    X<2>::Z<>::Y< char, double > b;
    
    0 讨论(0)
提交回复
热议问题