如何将C++枚举值标记为已弃用?

28

能否将枚举值标记为已弃用?

例如:

enum MyEnum {
    firstvalue = 0
    secondvalue,
    thirdvalue, // deprecated
    fourthvalue
};

第二种解决方案是使用ifdef分别实现MSVC和GCC的解决方案。


3
你希望发生什么?只需将其重命名,编译器就会发出错误提示... - Lindydancer
4
@Lindydancer:但这有点超出了弃用的范畴,不是吗?我认为重点是允许现有代码编译,但发出关于已弃用资源的警告。 - Fred Larson
8
如果足够好的话,类、函数和类型也不需要被废弃了。 - moala
@JCooper:听起来是一个有趣的解决方案,使用_Pragma,您确实可以在宏的扩展中包含一个编译指示。为什么不发表一个答案呢? - Matthieu M.
1
希望在C++0x中,正确的解决方案/技巧也能适用于作用域枚举 =] - David
显示剩余2条评论
9个回答

14

你可以这样做:

enum MyEnum {
    firstvalue = 0,
    secondvalue,
    thirdvalue, // deprecated
    fourthvalue
};
#pragma deprecated(thirdvalue)

那么每当变量被使用时,编译器将会输出以下内容:

warning C4995: 'thirdvalue': name was marked as #pragma deprecated

编辑
这看起来有点不正式,而且我没有GCC编译器确认(有人能为我做这件事吗?),但应该可以工作:

enum MyEnum {
    firstvalue = 0,
    secondvalue,
#ifdef _MSC_VER
    thirdvalue,
#endif
    fourthvalue = secondvalue + 2
};

#ifdef __GNUC__
__attribute__ ((deprecated)) const MyEnum thirdvalue = MyEnum(secondvalue + 1);
#elif defined _MSC_VER
#pragma deprecated(thirdvalue)
#endif

这是我的答案和MSalters的答案的结合


你知道是否可能在没有“#ifdef _MSC_VER”的情况下使其更好看吗?我正在尝试编写一个跨平台的宏来处理它,但这会让最终用户感到有些不愉快。如果最终用户能够使用自包含宏,那就太好了。 - László Papp
另一个没有ifdef的要点是,它可能会基于给定平台上底层枚举表示而破坏二进制兼容性。 - László Papp
好的解决方案,但不幸的是,这对于使用枚举作用域的C++11代码不起作用,例如:MyEnum test = MyEnum::thirdvalue; - Konstantin

14

您可以从C++14开始使用[[deprecated]]属性。

http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2013/n3760.html

因此,您将会这样写:

enum MyEnum {
    firstvalue = 0
    secondvalue,
    thirdvalue [[deprecated]],
    fourthvalue
};

当你使用显式赋值的方式废弃一个枚举值时,它会看起来像这样:

enum MyEnum {
    firstvalue = 0
    secondvalue,
    thirdvalue [[deprecated]] = 2,
    fourthvalue
};

6
一个例子如下: 枚举 E { A [[已弃用]] = 3, B }; - Michał Walenciak
1
从2022年的视角来看,这个答案应该被接受。@moala - usr1234567

7

3
好的,既然我们已经涉及到宏编程了,这是我的方法:-)
enum MyEnum
{
 foo,
 bar,
 baz
};

typedef __attribute__ ((deprecated))MyEnum MyEnum_deprecated;
#define bar ((MyEnum_deprecated) bar)

int main ()
{
    int a = foo; // yuck, why did C++ ever allow that...
    int b = bar;

    MyEnum c = foo;
    MyEnum d = bar;

    return 0;
}

这适用于gcc,并且不需要您破坏类型安全。不幸的是,它仍然通过宏滥用您的代码,所以呃。但据我所知,这是最好的选择。
Tom提出的建议更清晰(适用于MSVC,我想),但不幸的是,gcc唯一会给您返回的消息是“忽略pragma”。

这个能够在类或命名空间中使用枚举正常工作吗? - László Papp
由于这是一个宏hack,它甚至比您希望的更好地处理命名空间内的枚举。这就是宏的缺点,它们仅替换文本而不尊重命名空间。不幸的是,您无法将属性分配给单个枚举器,只能分配给整个枚举。因此,您必须进行此类黑客才能使其正常工作。 - Damon
实际上,如果不进一步增加用户的不便,它将无法使用命名空间,因此这个解决方案从我的角度来看是-1。此外,它有一个限制,即没有通用的宏可以包装它,因为宏内部的宏会破坏它... 它甚至会在C++11类枚举中进一步破坏。 - László Papp
问题既不是关于命名空间也不是关于C++11的。此外,我看不出为什么如果你正确限定使用C++11枚举,它就不能工作。另外,您为什么认为嵌套宏不起作用?它们在其他地方起作用,为什么在这种情况下却无法起作用呢? - Damon
Damon:如果嵌套宏,你会如何使其工作?就我所见,定义将在其定义的位置进行评估,而不是围绕重新定义的实际宏调用,请下次在我的名字旁加亮,否则我可能会错过回复,就像我在这里做的一样。 - László Papp

3
你可以在枚举声明之外声明枚举常量:
enum MyEnum {
    firstvalue = 0
    secondvalue,
    thirdvalue
};
__attribute__ ((deprecated)) const MyEnum fourthvalue = MyEnum(thirdvalue + 1);

这对于MSVC不起作用,也无法使用C++11类枚举。 - László Papp
如果编译器选择的底层类型MyEnum不足以容纳您分配的值(thirdvalue + 1),它也会中断。 - Tony Delroy

1

你也许可以使用一些宏编程技巧。

enum MyEnum {
    firstvalue = 0
    secondvalue,
    real_thirdvalue, // deprecated
    fourthvalue
};

template <MyEnum v>
struct real_value
{
    static MyEnum value()
    { 
        1 != 2U;  // Cause a warning in for example g++. Leave a comment behind for the user to translate this warning into "thirdvalue is deprecated"
        return v;
    }
};

#define thirdvalue (real_value<real_thirdvalue>::value());

当需要一个常量时,这种方法无法正常工作。


我不确定这是否有效,但是因为这种鲁莽的黑客行为值得赞扬,所以给你加一分。;-) - Paul R
为什么你要这样疯狂? - László Papp

1

使用编译器相关的编译指示:这里是GccVisual Studio的文档。


3
这个回答并没有解答问题,例如gcc允许将函数、类型和变量标记为“已弃用”,但没有提到如何将其应用于特定的枚举值。 - Paul R
2
遗憾的是,gcc不允许您为枚举值指定属性,只能在整个枚举类型上指定。当然,您可以创建两个枚举(一个带有弃用的值)作为解决方法,但这样它们将具有不同的类型。 - Damon
@Damon:也许加上一个参考资料,那就是一个很好的答案了。 - moala
参考编译器的错误信息(我以前尝试过完全相同的事情!)和文档中没有提到,而类型、变量和函数都有明确的提及。不幸的是,我的“解决方案”并不像听起来那么好,因为它必然涉及将枚举转换为整数。枚举具有类型(在C++0x中更强),篡改和欺骗类型系统并不是人们通常想要做的事情,这是有充分理由的。 - Damon
其实有一种相当类型安全的解决方案,但它也不太美观(使用typedef和定义)。 - Damon

0
我有一个解决方案(受Mark B启发),它利用了boost/serialization/static_warning.hpp。然而,我的解决方案允许将thirdvalue用作符号常量。它还会在每个尝试使用thirdvalue的地方产生警告。
#include <boost/serialization/static_warning.hpp>

enum MyEnum {
    firstvalue = 0,
    secondvalue,
    deprecated_thirdvalue, // deprecated
    fourthvalue
};

template <int line>
struct Deprecated
{
    BOOST_SERIALIZATION_BSW(false, line);
    enum {MyEnum_thirdvalue = deprecated_thirdvalue};
};

#define thirdvalue (static_cast<MyEnum>(Deprecated<__LINE__>::MyEnum_thirdvalue))

enum {symbolic_constant = thirdvalue};

int main()
{
    MyEnum e = thirdvalue;
}

在GCC上,我会收到警告,最终指向包含thirdvalue的罪犯行。

请注意,使用Deprecated模板使得“实例化位置”编译器输出行显示了废弃枚举的使用位置。

如果您能够想出一个在Deprecated模板内部生成可移植警告的方法,则可以省去对Boost的依赖。


0

C++14对标准语法属性(包括[[deprecated]])的支持比C++11更好,因为它允许枚举器也被注释(参见N3760)。这意味着OP的示例现在可以像这样:

enum MyEnum {
  firstvalue = 0,
  secondvalue,
  thirdvalue [[deprecated]],
  fourthvalue
};

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接