【问题标题】:Casting enum to int in a template parameter在模板参数中将枚举转换为 int
【发布时间】:2015-08-31 14:24:25
【问题描述】:

为了方便特定类,我定义了一个自定义enum,现在它妨碍了更通用的进程对其进行处理。

我应该如何执行这种类型转换?

// A templated value holder:
template <typename T>
struct Holder {
    T _value;
};

// A more general process..
template <typename T>
struct General {
    Holder<T> *holder;
};
// ..over integral types:
struct IntGeneral : General<int> {};
// Here is something interesting: I can tell that this process will work on
// any enum type. But I've found no way to make this explicit.

// Convenience for a particular case
typedef enum {One, Two, Three} Enum;
typedef Holder<Enum> Particular;


int main() { 

    Particular* a( new Particular { One } );
    IntGeneral ig { static_cast<Holder<int>*>(a) }; // compiler grumbles

    return EXIT_SUCCESS;
}

这是我得到的:

error: invalid static_cast from type ‘Particular* {aka Holder<Enum>*}’ to type ‘Holder<int>*’

有没有办法让我保持方便的Enum 并编译这段代码?


编辑:原来是XY problem。此处已接受 Y 的答案,并讨论了几个。 X 已移至another question

【问题讨论】:

  • 如果sizeof(Enum) != sizeof(int)你会发生什么?
  • @GlennTeitelbaum 非常正确。这就是我宁愿需要EnumGeneral : General&lt;something_that_would_mean_any_kind_of_enum&gt; 之类的东西的原因。有没有办法做到这一点?

标签: c++ templates enums generic-programming concept


【解决方案1】:

替换就够了:

IntGeneral ig { static_cast<Holder<int>*>(a) }; // compiler grumbles

与:

IntGeneral ig { new Holder<int>{a->_value} };

因为它是一个结构体,所以你可以访问它的值。就我个人而言,我会使用 getter 和 setter,但如前所述,这应该可以满足您的需求。

交替

你可以替换:

typedef Holder<Enum> Particular;

与:

typedef Holder<int> Particular;

由于int 可以容纳Enum

【讨论】:

  • 使用_value 不是解决方案,因为它在现实生活中是私有的,但重新定义typedef Holder&lt;int&gt; Particular 确实可以正常工作。我接受答案作为 Y 的答案,然后在另一个问题中重新表述 X。谢谢你:)
【解决方案2】:

如果T 可转换为U 并且T 是一个枚举,您可以添加一个模板化转换函数,该函数将允许Holder&lt;T&gt; 转换为Holder&lt;U&gt;

template <typename T>
struct Holder {
    T _value;

    template <typename U,
              typename = std::enable_if_t<
                std::is_convertible<T, U>::value && 
                std::is_enum<T>::value 
              >>
    operator Holder<U>() const {
        return {_value};
    }
};

可能只是可兑换性检查就足够了,具体取决于您的用例。

这会让你写:

Holder<int> i = Holder<Enum>{One};

但它不允许您将指针转换作为强制转换。您必须分配一个全新的Holder&lt;int&gt;* 来存储Holder&lt;Enum&gt;*

【讨论】:

  • .. 这太糟糕了。我认为这是因为问题在于链条的更高层:因为我可以告诉 IntGeneral 将适用于任何枚举类型。有没有办法可以将其定义为EnumGeneral : General&lt;something_that_would_mean_any_enum_type&gt;
  • @Iago-lito 你不能只拥有指向任何类型的Holder 的指针。除非你输入擦除......比如struct HolderBase { virtual ~HolderBase() = default; }; template &lt;typename T&gt; struct Holder : HolderBase { T _value; },然后拿着HolderBase*
【解决方案3】:

我认为让它工作的最简单方法是专门化Holder&lt;Enum&gt;,以便它可以隐式转换为Holder&lt;Int&gt;

template<>
struct Holder<Enum> {
  Enum _value;
  operator Holder<Int>() const { return { _value }; }
};

不过,这将复制枚举,因此根据 General 进程中的内容,这可能无法完全按照您的意愿进行。

【讨论】:

  • 确实,它可能不会。这让我觉得真正的问题在别处:看我给 Barry 的comment
  • 一种可能的替代方法是,与其让持有者公开一个公共值,不如让它公开setget 访问器到私有值?然后枚举将能够以多态方式构成一个 int,如果您尝试将其设置为我猜的错误值,它可能会抛出异常。 FWIW 我不太了解Holder 模板的用途。如果它的目的只是表示T 类型的值,那么为什么不直接使用T 类型的值呢?
  • 当然,Holder 更复杂,它的_value 实际上是私有的。我确实使用set 之类的东西来访问它,但是就像你说的那样,当将指针指向ig 时,如何使枚举“多态地作为 int 构成”?
  • 您的模板可能是这样的template&lt;typename T&gt; class Holder { private: T _value; public: template&lt;typename U = T&gt; U get() const { return _value; } template&lt;typename U&gt; void set(const U &amp; u) { _value = u; } };,然后当您有Holder&lt;Enum&gt; 时,您可以使用get&lt;int&gt;() 将其作为int 获取。
  • 嗯.. 已经有点像这样了。但是当我需要将Holder&lt;Enum&gt;* 转换为Holder&lt;int&gt;* 时,这并不能让编译器满意。还是我错过了什么?
【解决方案4】:

问题是static_cast 并不意味着转换不同类型的指针。我建议你使用reinterpret_cast,它应该可以工作。此外,您不能使用指针实例化 IntGeneral 类型的对象。

【讨论】:

  • 在此处使用reinterpret_cast 将导致未定义的行为。
  • 队长,默认情况下将一种指针类型转换为另一种不是安全操作,但这是 reinterpret_cast 的唯一目的(好吧,有一些说明)。 :)
  • 这不依赖于Particular 与任何Holder&lt;int&gt; 具有相同大小/内部数据组织的假设吗?如果是,那么情况并非如此,因为实际上Particular 更专业并且具有特定的成员。不过,我并没有在问题中明确这一点,也不要让事情变得复杂。
  • 是的,确实如此。如果是这种情况,就会出问题。
【解决方案5】:

您可以进行一些更改:

  1. 向“Holder”添加一个父类型,该类型可以引用泛型和特定类型。
  2. 为接收T的Holder定义一个构造函数
  3. 为接收Holder地址类型的General定义一个构造函数
  4. 对于 IntGeneral 的初始化列表(这是一个奇怪的名称,因为它不是通用的,但我明白了)以“Holder*”地址启动。

    struct HolderBase {};
    
    template <typename T>
    struct Holder : HolderBase{
         Holder(T value){ _value = value; }
         T _value;
    };
    
    template <typename T>
    struct General {
        General(Holder<T>* h){ holder = h; }
        Holder<T>* holder;
    };
    
    struct IntGeneral : General<int> {
        IntGeneral(Holder<int>* holder) : General<int>(holder){}
    };
    
    typedef enum { One, Two, Three } Enum;
    typedef Holder<Enum> Particular;
    
    
    int main() {
    
       HolderBase* a(new Particular{ One });
       IntGeneral ig{ static_cast<Holder<int>*>(a) };
    
       return 0;
    }
    

希望这就是你要找的东西

【讨论】:

    猜你喜欢
    • 2020-08-18
    • 1970-01-01
    • 1970-01-01
    • 2011-08-28
    • 2011-01-23
    • 2018-01-20
    • 1970-01-01
    • 2011-08-18
    • 2010-09-06
    相关资源
    最近更新 更多