将函数属性应用于析构函数
Apply function attribute to destructor
是否可以将属性应用于析构函数? Example:
#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
,它们会按预期创建警告。根据 the standard and cppreference,析构函数的语法 应该 允许前导和尾随属性。 (而且我认为这不会产生任何语法错误这一事实证实了这一点。)
在我的具体情况下,我有一个 UndefinedBehaviorSanitizer 构建 (-fsanitize=undefined
),我正尝试在析构函数上使用 __attribute__((no_sanitize("undefined")))
来抑制来自上游库的错误(我无法控制) .但是,我无法抑制错误,因为所有编译器似乎都忽略了这个属性。
我感觉答案会是一些非常令人不满意的东西,比如“允许编译器出于任何原因忽略任何属性”。如果是这样,有人可以建议解决方法吗?我宁愿不做像为整个目标禁用 UBSan 这样严厉的事情。
如果您用空主体替换默认析构函数,GCC 将发出带有介词属性的警告。后置属性不会触发警告。
struct Foo {
[[deprecated,gnu::noinline,gnu::error("test")]]
~Foo() { __asm__(""); }
};
int main() {
Foo* f = new Foo();
delete f;
return 0;
}
这个outputs:
<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 完全忽略析构函数,而是使用内置的析构逻辑来生成代码。我还没有查阅标准以了解规范如何发挥作用。
是否可以将属性应用于析构函数? Example:
#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
,它们会按预期创建警告。根据 the standard and cppreference,析构函数的语法 应该 允许前导和尾随属性。 (而且我认为这不会产生任何语法错误这一事实证实了这一点。)
在我的具体情况下,我有一个 UndefinedBehaviorSanitizer 构建 (-fsanitize=undefined
),我正尝试在析构函数上使用 __attribute__((no_sanitize("undefined")))
来抑制来自上游库的错误(我无法控制) .但是,我无法抑制错误,因为所有编译器似乎都忽略了这个属性。
我感觉答案会是一些非常令人不满意的东西,比如“允许编译器出于任何原因忽略任何属性”。如果是这样,有人可以建议解决方法吗?我宁愿不做像为整个目标禁用 UBSan 这样严厉的事情。
如果您用空主体替换默认析构函数,GCC 将发出带有介词属性的警告。后置属性不会触发警告。
struct Foo {
[[deprecated,gnu::noinline,gnu::error("test")]]
~Foo() { __asm__(""); }
};
int main() {
Foo* f = new Foo();
delete f;
return 0;
}
这个outputs:
<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 完全忽略析构函数,而是使用内置的析构逻辑来生成代码。我还没有查阅标准以了解规范如何发挥作用。