ICode9

精准搜索请尝试: 精确搜索
首页 > 其他分享> 文章详细

c – SFINAE和可变参数模板类

2019-08-28 01:05:18  阅读:134  来源: 互联网

标签:c c11 templates variadic-templates template-meta-programming


我正在创建一个继承自可变数量类的C类.定义了这些类的列表,例如:A,B.在C类的功能中,我需要从所有基类调用函数,但是对象可以是C< A,B>. ,C A或C B是H.所以,如果我将在C< B>中调用A类函数.我会收到一个错误.以下是类的示例以及我如何尝试解决问题:

    class A
    {
        int a;
    public:
        virtual void set_a(const int &value)
        {
            a = value;
        }
    protected:
        virtual int get_a()
        {
            return this->a;
        }
    };
    class B
    {
        int b;
    public:
        virtual void set_b(const int &value)
        {
            b = value;
        }
    protected:
        virtual int get_b()
        {
            return this->b;
        }
    };
    template<class ...T>
    struct Has_A
    {
        template<class U = C<T...>>
        static constexpr bool value = std::is_base_of < A, U > ::value;
    };

    template<class ...T>
    class C :
         virtual public T...
    {
    public:
    #define HAS_A Has_A<T...>::value

        void f()
        {
    #if HAS_A<>
            auto a = this->get_a();
    #endif
        auto b = this->get_b();
        cout << HAS_A<>;
    }
};

当我调用对象C的f()时,< A,B>它会跳过调用get_a(),但输出为true.

最初,我写了这个

template<class U = C<T...>>
typename std::enable_if<!std::is_base_of<A, U>::value, int>::type get_a()
{
    return -1;
}
template<class U = C<T...>>
typename std::enable_if<std::is_base_of<A,U>::value, int>::type get_a()
{
    return A::get_a();
}

但是我不想为A和B的所有函数重写它.让我们假设A还有10个函数.

有没有漂亮的解决方案?

P.S抱歉我的英语.我之前从未使用过SFINAE.
基本上我有一堆基因,我想为它们编写方便的包装,在那里可以配置他想要有机体的基因.

解决方法:

如果你可以使用C 17,双工的解决方案(如果constexpr())是(恕我直言)更好的解决方案.

否则,C 11或C 14,我不确定这是一个好主意,但我提出以下解决方案,因为在我看来这很有趣(并且有点变态).

首先,我提出了一个更通用的isTypeInList,而不是Has_A

template <typename...>
struct isTypeInList;

template <typename X>
struct isTypeInList<X> : public std::false_type
 { };

template <typename X, typename ... Ts>
struct isTypeInList<X, X, Ts...> : public std::true_type
 { };

template <typename X, typename T0, typename ... Ts>
struct isTypeInList<X, T0, Ts...> : public isTypeInList<X, Ts...>
 { };

我还建议使用简单的indexSequence

template <std::size_t...>
struct indexSequence
 { };

这是灵感来自std :: index_sequence(不幸的是)只能从C 14开始.

因此,在C< T ...>内,您可以使用定义模板

  template <typename X>
  using list = typename std::conditional<isTypeInList<X, Ts...>{},
                                         indexSequence<0u>,
                                         indexSequence<>>::type;

所以列表< A>是indexSequence< 0>如果A是T …可变参数列表的一部分,则indexSequence<> (空序列)否则.

现在你可以编写f()来简单地调用一个辅助函数f_helper(),它接收尽可能多的indexSequences,你需要检查多少个类型.

例如:如果您需要知道A和B是否是T … variadic列表的一部分,您必须编写f()如下

  void f ()
   { f_helper(list<A>{}, list<B>{}); }

现在f_helper()可以是私有函数,也可以

  template <std::size_t ... As, std::size_t ... Bs>
  void f_helper (indexSequence<As...> const &,
                 indexSequence<Bs...> const &)
   {
     using unused = int[];

     int a { -1 };
     int b { -1 };

     (void)unused { 0, ((void)As, a = this->get_a())... };
     (void)unused { 0, ((void)Bs, b = this->get_b())... };

     // do something with a and b
   }

这个想法是,如果A在T中,则As …为0,否则为空列表.

所以

int a { -1 };

使用假冒get_a()的值初始化a.

(void)unused { 0, ((void)As, a = this->get_a())... };

执行a = this-> get_a(),只执行一次iff(当且仅当)A在T … variadic列表中.

这个解决方案的有趣之处在于,当A不在可变参数列表中时,a = this-> get_a()不是问题.如果As …是一个空列表,那就不存在了.

以下是一个C 11完整的工作示例(我已经在Ts中重命名了… T … C的可变参数序列)

#include <utility>
#include <iostream>
#include <type_traits>

class A
 {
   private:
      int a;

   public:
      virtual void set_a (int const & value)
       { a = value; }

   protected:
      virtual int get_a ()
       { std::cout << "get_a()!" << std::endl; return this->a; }
 };

class B
 {
   private:
      int b;

   public:
      virtual void set_b (int const & value)
       { b = value; }

   protected:
      virtual int get_b ()
       { std::cout << "get_b()!" << std::endl; return this->b; }
 };

template <typename...>
struct isTypeInList;

template <typename X>
struct isTypeInList<X> : public std::false_type
 { };

template <typename X, typename ... Ts>
struct isTypeInList<X, X, Ts...> : public std::true_type
 { };

template <typename X, typename T0, typename ... Ts>
struct isTypeInList<X, T0, Ts...> : public isTypeInList<X, Ts...>
 { };

template <std::size_t...>
struct indexSequence
 { };

template <typename ... Ts>
class C : virtual public Ts...
 {
   private:
      template <typename X>
      using list = typename std::conditional<isTypeInList<X, Ts...>{},
                                             indexSequence<0u>,
                                             indexSequence<>>::type;

      template <std::size_t ... As, std::size_t ... Bs>
      void f_helper (indexSequence<As...> const &,
                     indexSequence<Bs...> const &)
       {
         using unused = int[];

         int a { -1 };
         int b { -1 };

         (void)unused { 0, ((void)As, a = this->get_a())... };
         (void)unused { 0, ((void)Bs, b = this->get_b())... };

         // do something with a and b
       }

   public:
      void f ()
       { f_helper(list<A>{}, list<B>{}); }
 };


int main()
 {
   C<>     c0;
   C<A>    ca;
   C<B>    cb;
   C<A, B> cab;

   std::cout << "--- c0.f()" << std::endl;
   c0.f();
   std::cout << "--- ca.f()" << std::endl;
   ca.f();
   std::cout << "--- cb.f()" << std::endl;
   cb.f();
   std::cout << "--- cab.f()" << std::endl;
   cab.f();
 }

标签:c,c11,templates,variadic-templates,template-meta-programming
来源: https://codeday.me/bug/20190828/1746605.html

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有