【问题标题】:c++, can I enable several trait class specializations using enable_if instead of copy-paste?c++,我可以使用 enable_if 而不是复制粘贴来启用几个特征类专业化吗?
【发布时间】:2018-05-07 22:41:46
【问题描述】:

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

// 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;

由于我只为ver3 定义了特征类,如果不明确定义ver1ver2 的特征类,我将无法实例化该类。但是,如果特征类对于 ver1 - ver 3 来说是完全相同的,那么是否有任何一种 enable_if 条件我必须使这个模板类对所有 revs &lt;= 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 查找的成本)?

【问题讨论】:

  • 您的目标并不完全清楚。当您不声明/定义最直接提供属性的成员时,您希望fieldTraits 和/或在helper 中使用TT 退回到不同的结果?基于以前的revision 值和/或一些整体默认值?如果您提供一张表格,说明您在其中为所有九个专业写了TT::field_val,可能会有所帮助?
  • 我修改了我的问题,如果可能的话,我希望帮助者回退到一个基地。例如,将修订版 3 特征用于未定义的修订版 2 或修订版 1 特征类。我认为答案是否定的……你不能这样做,但想验证一下。我认为这些专业的 field_val 值对我的问题不重要吗?
  • 对不起,我还是不明白。你希望TT::field_valhelper&lt;fields::One, revision::ver2&gt;()helper&lt;fields::Two, revision::ver2&gt;() 中做什么,为什么? (你有时有version,有时有revision,但看起来这些应该是一样的?)

标签: c++ c++11 templates polymorphism typetraits


【解决方案1】:

我很难理解你想要什么;请修改您的问题以查看详细信息(versionrevision 是否相同?fieldTraitstraitsClass 是否相同?)。

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

定义一个专业化
template <const field& T, revision R>
class fieldTraits;

Rval1val2val3 时,可能还有其他单个跟随值的特化。

假设您有四个修订版

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

您可以声明fieldTraits(作为struct,以使其更短)添加一个bool默认模板参数,表示如果R &lt;= 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; };

【讨论】:

  • 这真的很有帮助。这与 enable_if 有何不同?这会有效地禁用特征吗?
  • @user1945925 - 不,SFINAE 中没有禁用任何内容。它只用于特化:调用fieldTraits&lt;f, revision::ver1&gt;::field_val 被激活为true 的第三个(默认)值(因为ver1 &lt;= ver3 是真的)所以fieldTraits&lt;T, R, true&gt; 特化匹配。如果您尝试调用fieldTraits&lt;f, revision::ver1, false&gt;::field_val,您应该会收到一个错误,因为没有专业化匹配并且主版本已声明但未定义。
  • 我试图通过将声明设为template&lt; const field_t&amp; F, rev R, bool B = true &gt; class readerTraits; 来修改您的示例,然后尝试将部分类专业化创建为template&lt;rev R&gt; class readerTraits&lt; fields::One, R, (R &lt;= rev::V1_1) &gt;,但是我得到了error: template argument ‘(R &lt;= V1_1)’ involves template parameter(s)。因为 R 是非类型模板参数? stackoverflow.com/questions/5978617/…
  • @user1945925 - 完全正确;但你给了我一个不同的解决方案的想法......
  • @user1945925 - 根据您的想法回答修改后的添加解决方案(更灵活,恕我直言)
猜你喜欢
  • 2022-08-08
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2019-08-11
  • 1970-01-01
  • 1970-01-01
  • 2021-10-18
  • 1970-01-01
相关资源
最近更新 更多