繁体   English   中英

SFINAE和可变参数模板类

[英]SFINAE and variadic template classes

我正在创建一个继承自可变数量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<A,B> f()C<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();
}

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

有没有漂亮的解决方案?

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

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

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

我认为你可以用function-member-pointer做到这一点。

call_if_base仅在baseTT的基础call_if_base调用给定的函数指针。 但是,所有功能结果都会被忽略,并且至少需要一个参数。

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

    } 
};

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

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

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

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...> ,您可以using定义模板

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

因此,如果AT... variadic列表的一部分,则list<A>indexSequence<0>否则为indexSequence<> (空序列)。

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

例如:如果您需要知道AB是否是T... variadic列表的一部分,您必须编写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->get_a() ,只执行一次iff(当且仅当) AT... variadic列表中。

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

以下是一个C ++ 11完整的工作示例(我已经在Ts...重命名了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();
 }

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

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