C ++ :: std :: enable_if和虚拟

时间:2014-07-05 03:28:13

标签: c++ c++11

我已经扫描了答案,但......他们似乎是个愚蠢的问题。

我非常满意并完全理解(我不能强调这一点),为什么拥有一个类的模板虚拟成员毫无意义。

考虑抽象基类List(有...... LinkedListArrayList从中派生出来)

如果list存储的类型有一个identity的概念(不是字符串或int,没有任何明智的东西“==”,我不会把POD带到这里)你会想要一个方法
virtual bool contains(T& what) const;
virtual int index(T& what) const;
但是如果它是一个没有身份的类型,比如你想要的字符串或数字:
virtual int countOccurrences(T& what) const;
virtual int find(T& what, int occurrence=0) const;

说。

使用::std::enable_if无法完成此操作,因此您必须执行以下操作:

template<class T, bool HAS_IDENTITY> class List;
template<class T> class List<false> {
    virtual int countOccurrences(T& what) const=0;
    virtual int find(T& what, int occurrence=0) const=0;
    /*other stuff*/
};

template<class T> class List<true> {
    virtual bool contains(T& what) const =0;
    virtual int index(T& what) const =0;
    /*other stuff*/
};

这并不是那么糟糕,但是有很多代码重复,而且只有在必要的时候才会弄湿(反对DRY)。

如果我将公共代码隐藏在基类中,那就更好了。

我的问题涉及使用这种方法缩放,这里我们有一个bool,给出2个特化,假设我有n个bool然后有2 ^ n个特化,我看不到我需要超过4个的情况,但这仍然涉及16个课程! 8比3,这不是很好。

假设我有一个enum和一个bool,那么我有2 * enum count专长。

它快速增长。

以前我们已经使用宏来定义类,并且它在类名中使用##运算符来实际上将其作为模板进行修改。我必须说虽然我现在非常喜欢enable_if和朋友......

是否有某种模式可以让我告诉我解决这个问题?

2 个答案:

答案 0 :(得分:1)

只是一个q&amp; d hack,但它应该提供一些提示。

我知道一个人甚至可以摆脱那个丑陋的&#34; Dummy&#34; param,但我现在没有看到

编辑7月6日

我使ansatz使用起来更加无缝。 概念&#34;身份&#34;的编译时测试,问题揭幕者显然是针对什么,需要编译时测试

//T t1, t2;
(t1 == t2) == (&t1 == &t2);

这是不可能的。 因此,我介绍了功能列表的概念,以便于手动分配这些功能。

#include <iostream>
#include <typeinfo>
#include <type_traits>
#ifdef __GNUG__
#include <cxxabi.h>
auto type_str = [](const std::type_info& ti) {
    int stat;
    return abi::__cxa_demangle(ti.name(), 0, 0, &stat);
};
#else 
#warning untested
auto type_str = [](const std::type_info& ti) {
     return ti.name();
};
#endif


typedef int Feature;

const Feature HAS_IDENTITY = 1;
const Feature HAS_FOOBAR = 2;
const Feature HAS_NO_IDENTITY = -HAS_IDENTITY;
const Feature HAS_NO_FOOBAR = -HAS_FOOBAR;
const Feature _TERM_ = 0;


template<typename T, Feature F>
struct has_feature : std::false_type {};

template<int N , int M>
struct is_greater {
    constexpr static bool value = N > M;
};

namespace  detail {
    template<class T, Feature... Fs> struct List {};  // primary template
    template<class T, Feature F>
    struct List<T,F> {};

    template<class T, Feature F, Feature... Fs> 
    struct List<T,F,Fs...> 
        : virtual public 
                std::conditional<
                    has_feature<T,F>::value,
                    List<T, F>,
                    List<T, -F> 
                >::type,
        virtual public 
                std::conditional<
                    is_greater<sizeof...(Fs),0>::value,
                    List<T, Fs...>, 
                    List<T, _TERM_>
                > ::type {};

    template<class T> struct List<T, _TERM_> {};

    template<class T> 
    struct List<T,HAS_NO_FOOBAR> {
        virtual std::string hello() const /* = 0;*/ {
            return std::string("\"What the foo is FOOBAR?\", askes ") + type_str(typeid(T));
        }
    };

    template<class T> 
    struct List<T,HAS_FOOBAR> {
        virtual std::string hello() const /* = 0;*/ {
            return std::string("\"For sure I'm FOOBAR\", says ") + type_str(typeid(T));
        }
    };


    template<class T> 
    struct List<T,HAS_NO_IDENTITY> {
        virtual int index(const T& what) const /* = 0;*/ {
            return 137;
        }
    };

    template<class T> 
    struct List<T,HAS_IDENTITY> {
        virtual int index(const T& what) const /* = 0;*/ {
            return 42;
        }
    };

    template<typename T>
    using Feature_Aware_List = List<T,HAS_IDENTITY,HAS_FOOBAR, /* all Features incuding*/_TERM_>;
} //namespace detail

template<typename T>
using List = detail::Feature_Aware_List<T>;

struct Gadget { 
    bool operator== (const Gadget& rhs) const {
        return this == &rhs;
    }
};     

struct Gimmick { 
    bool operator== (const Gimmick& rhs) const {
        return this == &rhs;
    }
};     

template<Feature F>
struct FeatureList {};

template<>
struct FeatureList<HAS_IDENTITY>
    : public Gadget, 
      public Gimmick 
      /**/ 
{};

#include <valarray>
template<>
struct FeatureList<HAS_FOOBAR>
    : public std::valarray<float> 
      /**/ 
{};

template<class T> 
struct has_feature<T, HAS_IDENTITY> 
    : public std::conditional<
        std::is_base_of<T, FeatureList<HAS_IDENTITY>>::value,
        std::true_type,
        std::false_type
    >::type {};

template<class T> 
struct has_feature<T, HAS_FOOBAR> 
    : public std::conditional<
        std::is_base_of<T, FeatureList<HAS_FOOBAR>>::value,
        std::true_type,
        std::false_type
    >::type {};


int main() {
    List<Gadget> l1 ;
    List<std::valarray<float>> l2;
    std::cout << l1.hello() << " #" << l1.index(Gadget()) << std::endl;
    std::cout << l2.hello() << " #" << l2.index(std::valarray<float>()) << std::endl;

}

输出:

"What the foo is FOOBAR?", askes Gadget #42
"For sure I'm FOOBAR", says std::valarray<float> #137

应该是自我解释,没有具体的&#34;列表&#34;实现了功能,这只是模拟

答案 1 :(得分:1)

您可以使用模板政策:

template<class T, bool HAS_IDENTITY> class ListIdentityPolicy;
template<class T> class ListIdentityPolicy<T, false> {
    virtual int countOccurrences(T& what) const = 0;
    virtual int find(T& what, int occurrence = 0) const = 0;
};
template<class T> class ListIdentityPolicy<T, true> {
    virtual bool contains(T& what) const = 0;
    virtual int index(T& what) const = 0;
};

template<class T, bool HAS_FOOBAR> struct ListFoobarPolicy;
template<class T> struct ListFoobarPolicy<T, false> {
    virtual void foo() = 0;
};
template<class T> struct ListFoobarPolicy<T, true> {
    virtual void bar() = 0;
};

template <class T> class List
    : public ListIdentityPolicy<T, HasIdentity<T>::value>
    , public ListFoobarPolicy<T, HasFoobar<T>::value>
{
public:
    /*other stuff*/
};

HasIdentityHasFoobar是您要定义的类型特征,每个特征都包含一个static const bool value,表示T是否具有相应的属性。


或者,您可以为List提供非虚拟公共API,并在实现中隐藏动态分派:

template <class T> class List
{
public:
    enum Impl {
        LinkedList = 0,
        ArrayList,
    };
    List(Impl i) : pimpl(makePimpl(i)) {}
    List(List const& other) : pimpl(other.pimpl->clone())
    List& operator=(List const& other) { pimpl = other.pimpl->clone(); }

    int count(T& what) const
    { static_assert(! HasIdentity<T>::value, "oops"); return pimpl->count(what); }
    int find(T& what, int n = 0) const
    { static_assert(! HasIdentity<T>::value, "oops"); return pimpl->find(what, n); }
    bool contains(T& what) const
    { static_assert(HasIdentity<T>::value, "oops"); return pimpl->contains(what); }
    int index(T& what) const
    { static_assert(HasIdentity<T>::value, "oops"); return pimpl->index(what); }
    void foo()
    { static_assert(! HasFoobar<T>::value, "oops"); pimpl->foo(); }
    void bar()
    { static_assert(HasFoobar<T>::value, "oops"); pimpl->bar(); }

private:
    struct AbstractPimpl
    {
        virtual std::unique_ptr<AbstractPimpl> clone() const = 0;
        virtual int count(T& what) const = 0;
        virtual int find(T& what, int n = 0) const = 0;
        virtual bool contains(T& what) const = 0;
        virtual int index(T& what) const = 0;
        virtual void foo() = 0;
        virtual void bar() = 0;
    };

    struct LinkedListPimpl : public AbstractPimpl
    {
        std::unique_ptr<AbstractPimpl> clone() override;
        int count(T& what) const override;
        int find(T& what, int n = 0) const override;
        bool contains(T& what) const override;
        int index(T& what) const override;
        void foo() override;
        void bar() override;
        /* ... */
    };

    struct ArrayListPimpl : public AbstractPimpl
    {
        std::unique_ptr<AbstractPimpl> clone() override;
        virtual int count(T& what) const override;
        virtual int find(T& what, int n = 0) const override;
        virtual bool contains(T& what) const override;
        virtual int index(T& what) const override;
        virtual void foo() override;
        virtual void bar() override;
        /* ... */
    };

    std::unique_ptr<AbstractPimpl> pimpl;

    static std::unique_ptr<AbstractPimpl> makePimpl(Impl i) {
        switch (i) {
            LinkedList: default:
            return std::make_unique<LinkedListPimpl>();
            ArrayList:
            return std::make_unique<ArrayListPimpl>();
        }
    }
};