【问题标题】:SFINAE and variadic template classesSFINAE 和可变参数模板类
【发布时间】:2019-01-15 21:18:33
【问题描述】:

我正在创建一个从可变数量的类继承的class C。定义了这些类的列表,例如:A,B。在class C 的函数中,我需要调用所有基类的函数,但对象可以是C<A,B>C<A>C<B>,所以如果我在C<B> 中调用class 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&lt;A,B&gt;f() 时,它会跳过调用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();
}

但我不想为AB 的所有函数重写这个。假设A 还有10 个函数。

有什么好的解决办法吗?

P.S 对不起我的英语。我以前从未使用过 SFINAE。 基本上我有一堆基因,我想为它们编写方便的包装,可以配置他希望有机体拥有的基因。

【问题讨论】:

  • 我不认为你的代码会编译:类模板C在类模板Has_A中使用时没有定义。
  • 你需要这个做什么?这看起来很奇怪。

标签: c++ c++11 templates variadic-templates template-meta-programming


【解决方案1】:

在当前标准中,这是微不足道的:

void f() {
    if constexpr(Has_A<T...>::value) {
        auto a = get_a();
    }
    auto b = get_b();
}

【讨论】:

    【解决方案2】:

    如果您可以使用 C++17,bipll 的解决方案 (if 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&lt;T...&gt;里面,你可以定义模板using

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

    如果AT... 可变参数列表的一部分,则list&lt;A&gt;indexSequence&lt;0&gt;,否则indexSequence&lt;&gt;(空序列)。

    现在您可以编写 f() ,它只需调用一个辅助函数 f_helper() 即可接收与您需要检查的类型一样多的 indexSequences。

    例如:如果你想知道AB是否是T...可变参数列表的一部分,你必须写f()如下

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

    现在f_helper() 可以是private 函数并且可以是

      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
       }
    

    如果AT... 中,则As...0,否则为空列表。

    所以

    int a { -1 };
    

    用你的假get_a()的值初始化a

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

    被执行a = this-&gt;get_a(),只执行一次,当且仅当AT...可变参数列表中。

    这个解决方案的有趣之处在于,当A 不在可变参数列表中时,a = this-&gt;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();
     }
    

    【讨论】:

    • 经典的 TL:DR;我害怕。
    【解决方案3】:

    我认为你可以使用函数成员指针来做到这一点。

    call_if_base 仅当baseTT 的基时才调用给定的函数指针。但是所有的函数结果都会被忽略,它至少需要一个参数。

    template <class baseT, class T, typename funcT, class ...Args>
    typename std::enable_if<std::is_base_of<baseT, T>::value, void>::type call_if_base(T& obj, funcT func, Args... args) {
        (dynamic_cast<baseT&>(obj).*func)(args...);
    }
    
    template <class baseT, class T, typename funcT, class ...Args>
    typename std::enable_if<!std::is_base_of<baseT, T>::value, void>::type call_if_base(T& obj, funcT func, Args... args) {
    
    }   
    
    template<class ...T>
    class C :
         virtual public T...
    {
    public:
        void set(const int &value) {
            call_if_base<A, C>(*this, &A::set_a, 0);
            call_if_base<B, C>(*this, &B::set_b, 5);
        }
    };
    

    或作为成员函数

    template<class ...T>
    class C :
         virtual public T...
    {       
    public:
        void set(const int &value) {
            call_if_base<A>(&A::set_a, 0);
            call_if_base<B>(&B::set_b, 5);
        }
    protected:
        template <class baseT, typename funcT, class ...Args>
        typename std::enable_if<std::is_base_of<baseT, C>::value, void>::type call_if_base(funcT func, Args... args) {
            (dynamic_cast<baseT&>(*this).*func)(args...);
        }
    
        template <class baseT, typename funcT, class ...Args>
        typename std::enable_if<!std::is_base_of<baseT, C>::value, void>::type call_if_base(funcT func, Args... args) {
    
        } 
    };
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2015-12-14
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多