首页 > 解决方案 > c++,我可以使用 enable_if 而不是复制粘贴来启用几个特征类专业化吗?

问题描述

所以我有一个非模板base类,其中包含它的方法的“默认”设置。然后我尝试将继承与模板类一起使用。这是用于说明的示例代码片段。

// enums for as a template selector
enum class version
{
    ver1,
    ver2,
    ver3
};

// Base class with fabricated methods
struct base
{
    virtual void propertyOne()
    {
        // some default action
    }

    virtual void propertyTwo()
    {
        // some default action
    }
};  



// derived class
template <version V>
struct derived : public base
{
    virtual void propertyOne()
    {
        helper< One, V >();
    }

    virtual void propertyTwo()
    {
        helper< Two, V >();
    }
}

我正在使用辅助函数对类特征中使用的不同“字段”执行“通用”算法。

例如:一个字段与此类似

struct field
{
    int thingone;

    constexpr field(int i):thingone(i){}
};

在 c++ 11 中,为了给字段实例提供外部链接,我将它们包装为另一个结构的静态成员(c++14 放宽了这些规则,哦,好吧)。我这样做的全部原因是因为我需要它的常量表达式值(例如,成员变量thingone 需要作为另一个方法的模板参数,它要求它是一个常量表达式)。

struct fields
{
    static constexpr field One{1};
    static constexpr field Two{2};
};

// defining trait class from structure above
template< const field& T, revision R >
class fieldTraits;

// sample fieldTrait definitions for illustrative purposes

template< >
class fieldTraits< fields::One, revision::ver3>
{
    public:
        // Let's say I have common field names
        // with different constants that I want to plug
        // into the "helper" algorithm
        static constexpr size_t field_val = 1; 
};

template< >
class fieldTraits< fields::Two, revision::ver1>
{
    public:
        // Let's say I have common field names
        // with different constants that I want to plug
        // into the "helper" algorithm
        static constexpr size_t field_val = 1; 
};  

// Main guts of the class methods above
template< const field& F, revision R, typename TT = traitClass<F,R> >
void helper()
{
    // Let's pretend I'm doing something useful with that data
    std::cout << F.thingone << std::endl;
    std::cout << TT::field_val << std::endl;
}

我遇到的问题是尝试实例化例如

derived<revision::rev1> l_derived;

由于我只定义了 trait 类,ver3我无法在不明确定义 trait 类的情况下实例化该类。但是,如果特征类对于-来说是完全相同的,那么是否有任何一种 enable_if 条件我必须使这个模板类对所有人都有效? ver1ver2ver1ver 3revs <= ver3

我在 traits_type 标头中找不到任何内容,该标头主要提供编译时“类型”检查,例如std::is_same等。

我知道一种选择是复制粘贴特征类ver1-ver3但这似乎是多余的,因为我想避免复制粘贴重复的代码。

另一种选择是为每个修订创建不同的类并利用动态多态性,我可以为每个修订定义一个类。然后我只需要在需要的地方包含特征修订更改。例如,

class derived_ver1 : public base
{
    virtual void propertyOne()
    {
        helper< fields::One, revision::ver1 >();
    }

    virtual void propertyTwo()
    {
        helper< fields::Two, revision::ver1 >();
    }
};

class derived_ver2 : public derived:ver1
{
    virtual void propertyTwo()
    {
        helper< fields::Two, revision::ver2 >();
    }
};

class derived_ver3 : public derived:ver2
{
    virtual void propertyTwo()
    {
        helper< Two, revision::ver3 >();
    }
};

在此示例propertyOne()中,可以将修订版 1 中的特征类重用于修订版 2 和修订版 3,因为它没有更改过去的修订版 1(并且避免复制粘贴特征)。

有没有更好的设计我可以接近?

总而言之:有没有办法使用我原来的模板继承并使用一些模板功能(例如std::enable_if)为未定义的修订重用一个特征类。而不是为每个修订明确定义一个特征(这会导致复制粘贴)。

或者使用动态多态的第二种方法是更好的方法(我读到,增加了 vtable 查找的成本)?

标签: c++c++11templatespolymorphismtypetraits

解决方案


我发现很难理解你想要什么;请修改您的问题以检查详细信息(version并且revision相同?fieldTraits并且traitsClass相同?)。

无论如何,如果我理解正确,您想为

template <const field& T, revision R>
class fieldTraits;

when Ris val1or val2or val3and, may be, other specializations for single following values。

假设您有四个修订版

enum class revision { ver1, ver2, ver3, ver4 };

您可以声明fieldTraits(作为struct, 使其更短)添加一个bool默认模板参数,说明 ifR <= revision::ver3

template <field const & T, revision R, bool = (R <= revision::ver3)>
struct fieldTraits;

现在您可以为ver1ver2ver3

template <field const & T, revision R>
struct fieldTraits<T, R, true>
 { static constexpr size_t field_val = 1; };

和专业化ver4

template <field const & T>
struct fieldTraits<T, revision::ver4>
 { static constexpr size_t field_val = 2; };

以下是一个简化但完整的示例

#include <iostream>

enum class revision { ver1, ver2, ver3, ver4 };

struct field
 {
   int thingone;

   constexpr field (int i) : thingone(i)
    { }
 };

template <field const & T, revision R, bool = (R <= revision::ver3)>
struct fieldTraits;

// version ver1, ver2, ver3 cases
template <field const & T, revision R>
struct fieldTraits<T, R, true>
 { static constexpr size_t field_val = 1; };

// version ver4 case
template <field const & T>
struct fieldTraits<T, revision::ver4>
 { static constexpr size_t field_val = 2; };

static constexpr field f{1};

int main ()
 {    
   std::cout << "ver1: " << fieldTraits<f, revision::ver1>::field_val
      << std::endl;
   std::cout << "ver2: " << fieldTraits<f, revision::ver2>::field_val
      << std::endl;
   std::cout << "ver3: " << fieldTraits<f, revision::ver3>::field_val
      << std::endl;
   std::cout << "ver4: " << fieldTraits<f, revision::ver4>::field_val
      << std::endl;
 }

那个打印

ver1: 1
ver2: 1
ver3: 1
ver4: 2

- 编辑 -

根据 OP 的评论,我提出了一些不同的解决方案

template <field const & T, revision R, typename = std::true_type>
struct fieldTraits;

// version ver1, ver2, ver3 cases
template <field const & T, revision R>
struct fieldTraits<T, R, std::integral_constant<bool, (R <= revision::ver3)>>
 { static constexpr size_t field_val = 1; };

// version ver4 case
template <field const & T>
struct fieldTraits<T, revision::ver4>
 { static constexpr size_t field_val = 2; };

推荐阅读