如何将 C++ 类的一个成员函数标记为废弃?

172

我有一个在C++接口中的方法,我想弃用它,并编写便携式代码。

当我在Google上搜索时,我得到了一个仅适用于Microsoft的解决方案:#pragma deprecated__declspec(deprecated)

如果没有通用或完全可移植的弃用解决方案,我将接受一个可用于多个特定编译器(如MSVC和GCC)的“第二奖励解决方案”。

7个回答

235

在 C++14 中,你可以使用 [[deprecated]] 属性(参见第 7.6.5 节 [dcl.attr.deprecated])标记一个函数为已弃用。

deprecated 属性标记的名字和实体仍然被允许使用,但因某种原因而不推荐使用。

例如,下面的函数 foo 已经被弃用:

[[deprecated]]
void foo(int);

可以提供一个描述名称或实体为何被弃用的消息:

[[deprecated("Replaced by bar, which has an improved interface")]]
void foo(int);

该消息必须是字符串字面值。

更多细节请参见“在C++14中标记为已弃用”


1
你能在宏中使用[[deprecated]]吗? - Daniel Ryan
3
@Zammbi,你应该可以做到,因为宏在编译之前由预处理器处理。[[已弃用]]应该出现(并让编译器输出相关警告)在宏被评估的地方。 - Florian Castellane
2
那么C++11呢? - warchantua
如何将这个宏GRIKIFY标记为已弃用? #define GRIKIFY(x) if (x) Grike(x) - Eljay

131
这应该可以解决问题:
#ifdef __GNUC__
#define DEPRECATED(func) func __attribute__ ((deprecated))
#elif defined(_MSC_VER)
#define DEPRECATED(func) __declspec(deprecated) func
#else
#pragma message("WARNING: You need to implement DEPRECATED for this compiler")
#define DEPRECATED(func) func
#endif

...

//don't use me any more
DEPRECATED(void OldFunc(int a, float b));

//use me instead
void NewFunc(int a, double b);

然而,如果函数返回类型中有逗号,例如std::pair<int, int>,则会遇到问题,因为这将被预处理器解释为向DEPRECATED宏传递2个参数。在这种情况下,您需要使用typedef重新定义返回类型。
编辑:较简单的(但可能不太兼容)版本在这里

8
不使用#error,使用#define DEPRECATED(func) func 会更好。 - CesarB
2
是的,我会选择“#warning You need to implement DEPRECATED for this compiler”或类似的内容。如果不可能,那么移植者可以使用“#define DEPRECATED(FUNC) FUNC”,并且不使用它。 - Steve Jessop
2
遗憾的是,在C++中没有标准的方法来输出编译警告 :P #pragma message就只能用了。 - Michael Platings
4
gcc的属性语法现在允许属性与__declspec(deprecated)放置在相同的位置,因此宏可以简化。 - bames53
1
不需要向宏传递参数,只需将其声明为#define DEPRECATED __declspec(deprecated)并使用DEPRECATED void MyApi() - DarkWanderer
显示剩余8条评论

68
以下是我2008年回答的简化版本:

这里是我2008年回答的简化版:

#if defined(__GNUC__) || defined(__clang__)
#define DEPRECATED __attribute__((deprecated))
#elif defined(_MSC_VER)
#define DEPRECATED __declspec(deprecated)
#else
#pragma message("WARNING: You need to implement DEPRECATED for this compiler")
#define DEPRECATED
#endif

//...

//don't use me any more
DEPRECATED void OldFunc(int a, float b);

//use me instead
void NewFunc(int a, double b);

参见:


21
你如何“[[deprecate]]”你的废弃宏? :-) (翻译说明:原句中的[[deprecate]]为英文单词,表示“废弃”或“弃用”。在翻译过程中,我保留了原句中的符号和表情,并将“macro”翻译为“宏”,使得整个句子更加通俗易懂。) - graham.reeds
4
为什么你会把它再次发布?我看不出这两个答案之间有任何重大的区别。 - Tomáš Zato
4
不必将其包装在函数周围,因此应将其写为DEPRECATED void foo(...);而不是DEPRECATED(void foo(...)); - dshepherd
14
你应该编辑你2008年的回答,而不是发布一个新的回答。 - Yakov Galka
4
这个答案可能没有我之前的回答兼容性广,因此我将其单独添加。 - Michael Platings
显示剩余5条评论

23
在GCC中,您可以使用属性"deprecated"来声明函数,如下所示:
void myfunc() __attribute__ ((deprecated));

在 .c 文件中使用该函数时,这将触发编译时警告。

更多信息可以在“诊断编译指令”下找到。


13

以下是2018年更全面的答案。

现在,许多工具不仅允许您将某些内容标记为已弃用,而且还提供了消息功能。这使您能够告诉人们何时出现过期的内容,并可能指向替代方案。

编译器支持仍有很大差异:

  • C++14支持[[deprecated]]/[[deprecated(message)]]
  • __attribute__((deprecated))由GCC 4.0+和ARM 4.1+支持。
  • __attribute__((deprecated))__attribute__((deprecated(message)))适用于:
    • GCC 4.5+
    • 几个伪装成GCC 4.5+(通过设置__GNUC__/__GNUC_MINOR__/__GNUC_PATCHLEVEL__)的编译器
    • 至少从16开始的英特尔C/C++编译器(您不能信任__GNUC__/__GNUC_MINOR__,它们仅将其设置为安装的GCC版本)
    • ARM 5.6+
  • 自Visual Studio 2003(13.10)以来,MSVC支持__declspec(deprecated)
  • 自Visual Studio 2005(14.0)以来,MSVC支持__declspec(deprecated(message))

在基于__has_cpp_attribute(gnu::deprecated)的C++11最新版本的clang中,您还可以使用[[gnu::deprecated]]

我在Hedley中有一些宏,可自动处理所有这些内容,并在新版本中保持最新状态,但当前版本(v2)如下:

#if defined(__cplusplus) && (__cplusplus >= 201402L)
#  define HEDLEY_DEPRECATED(since) [[deprecated("Since " #since)]]
#  define HEDLEY_DEPRECATED_FOR(since, replacement) [[deprecated("Since " #since "; use " #replacement)]]
#elif \
  HEDLEY_GCC_HAS_EXTENSION(attribute_deprecated_with_message,4,5,0) || \
  HEDLEY_INTEL_VERSION_CHECK(16,0,0) || \
  HEDLEY_ARM_VERSION_CHECK(5,6,0)
#  define HEDLEY_DEPRECATED(since) __attribute__((__deprecated__("Since " #since)))
#  define HEDLEY_DEPRECATED_FOR(since, replacement) __attribute__((__deprecated__("Since " #since "; use " #replacement)))
#elif \
  HEDLEY_GCC_HAS_ATTRIBUTE(deprcated,4,0,0) || \
  HEDLEY_ARM_VERSION_CHECK(4,1,0)
#  define HEDLEY_DEPRECATED(since) __attribute__((__deprecated__))
#  define HEDLEY_DEPRECATED_FOR(since, replacement) __attribute__((__deprecated__))
#elif HEDLEY_MSVC_VERSION_CHECK(14,0,0)
#  define HEDLEY_DEPRECATED(since) __declspec(deprecated("Since " # since))
#  define HEDLEY_DEPRECATED_FOR(since, replacement) __declspec(deprecated("Since " #since "; use " #replacement))
#elif HEDLEY_MSVC_VERSION_CHECK(13,10,0)
#  define HEDLEY_DEPRECATED(since) _declspec(deprecated)
#  define HEDLEY_DEPRECATED_FOR(since, replacement) __declspec(deprecated)
#else
#  define HEDLEY_DEPRECATED(since)
#  define HEDLEY_DEPRECATED_FOR(since, replacement)
#endif

如果您不想使用Hedley,而想要摆脱*_VERSION_CHECK*_HAS_ATTRIBUTE宏,那么将其除去的方法留作练习(我写了Hedley主要是因为我不想经常考虑这个)。

如果您使用GLib,则可以使用G_DEPRECATEDG_DEPRECATED_FOR宏。虽然它们没有Hedley的宏那么强大,但如果您已经在使用GLib,则无需添加其他内容。


4
处理便携式项目时,你几乎不可避免地需要为一系列平台提供预处理的备选方案。需要使用诸如#ifdef this #ifdef that等条件语句。 在这样的部分中,您可以有条件地定义一种弃用符号的方法。我的首选通常是定义一个“warning”宏,因为大多数工具链支持自定义编译器警告。然后,您可以继续使用特定的警告宏来表示弃用等问题。 对于支持专用弃用方法的平台,您可以使用该方法替代警告。

1
对于Intel编译器v19.0,使用以下代码,因为__INTEL_COMPILER的值为1900:
#  if defined(__INTEL_COMPILER)
#    define DEPRECATED [[deprecated]]
#  endif

以下是支持的语言级别:
  • C++17支持(/Qstd=c++17)
  • C++14支持(/Qstd=c++14)
  • C++11支持(/Qstd=c++11)
  • C11支持(/Qstd=c11)
  • C99支持(/Qstd=c99)
英特尔编译器似乎存在一个错误,即它不支持某些其他编译器支持的特定语言元素上的[[deprecated]]属性。例如,在GitHub上使用Intel Compiler v19.0编译remarkly superb {fmtlib/fmt}库的v6.0.0版本时,它会出错。然后查看GitHub提交中的修复

这是不正确的;在ICC的C模式下,C++属性无法工作。示例。另一方面,__attribute__((deprecated))可以在C和C++中使用,至少从ICC 13.0开始,可能更早(Intel倾向于不记录此类信息,因此我不能确定)。 - nemequ

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