首页 > 解决方案 > 将函数属性应用于析构函数

问题描述

是否可以将属性应用于析构函数? 示例

#if defined (__GNUC__) && !defined(__clang__)
#  define TEST_PRE_ATTR [[deprecated]] __attribute__((deprecated))
#  define TEST_POST_ATTR __attribute__((error("test")))
#elif defined(_MSC_VER) && !defined(__clang__)
#  define TEST_PRE_ATTR [[deprecated]] __declspec(deprecated)
#  define TEST_POST_ATTR
#elif defined (__clang__)
#  define TEST_PRE_ATTR [[deprecated]] __attribute__((deprecated))
#  define TEST_POST_ATTR __attribute__((diagnose_if(true, "test", "error")))
#endif

struct Foo {
    //TEST_PRE_ATTR 
    void bar() 
    //TEST_POST_ATTR 
    {}

    TEST_PRE_ATTR
    ~Foo()
    TEST_POST_ATTR
    = default;
};

int main() {
    Foo* f = new Foo();
    f->bar();
    delete f;
    return 0;
}

GCC、Clang 和 MSVC 忽略Foo::~Foo. 如果您将相同的属性应用于Foo::bar,它们会按预期创建警告。根据标准cppreference,析构函数的语法应该允许前导和尾随属性。(我想这不会产生任何语法错误的事实证实了这一点。)

在我的具体情况下,我有一个 UndefinedBehaviorSanitizer 构建(-fsanitize=undefined),我试图__attribute__((no_sanitize("undefined")))在析构函数上使用来抑制来自上游库的错误(超出我的控制)。但是,我无法抑制错误,因为所有编译器似乎都忽略了这个属性。

我有一种感觉,答案将是非常不令人满意的,例如“允许编译器出于任何原因忽略任何属性”。如果是这种情况,有人可以提出解决方法吗?我宁愿不做像为整个目标禁用 UBSan 那样严厉的事情。

标签: c++attributes

解决方案


如果将默认析构函数替换为空主体,GCC 将发出带有介词属性的警告。后置属性不会触发警告。

struct Foo {
    [[deprecated,gnu::noinline,gnu::error("test")]]
    ~Foo() { __asm__(""); }
};

int main() {
    Foo* f = new Foo();
    delete f;
    return 0;
}

输出

<source>: In function 'int main()':
<source>:8:12: warning: 'Foo::~Foo()' is deprecated [-Wdeprecated-declarations]
    8 |     delete f;
      |            ^
<source>:3:5: note: declared here
    3 |     ~Foo() { __asm__(""); }
      |     ^
<source>:8:12: error: call to 'Foo::~Foo' declared with attribute error: test
    8 |     delete f;
      |            ^
Compiler returned: 1

__asm__防止内联,以确保gnu::error触发诊断。Clang 的行为类似;根据后者的诊断,后置属性显然应用于this(!) 的类型。另一方面,MSVC 总是忽略[[deprecated]]析构函数的属性。(我并不感到惊讶,因为弃用析构函数没有什么意义。他们可能从来没有费心去测试这个。)

我想默认析构函数会使 GCC 和 Clang 完全忽略析构函数,而是使用内置的析构逻辑来生成代码。我还没有查阅标准以了解规范如何发挥作用。


推荐阅读