Is it possible to apply attributes to destructors? 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, and MSVC ignore all of these attributes on Foo::~Foo
. If you apply the same attributes to Foo::bar
, they create warnings as expected. According to the standard and cppreference, the grammar for destructors should allow leading and trailing attributes. (And I suppose the fact that this does not produce any syntax errors confirms that.)
In my specific case, I have an UndefinedBehaviorSanitizer build (-fsanitize=undefined
) and I am trying to use __attribute__((no_sanitize("undefined")))
on a destructor to suppress an error from an upstream library (out of my control). However, I can't suppress the error because all compilers seem to be ignoring this attribute.
I have a feeling that the answer will be something extremely unsatisfying like "compilers are allowed to ignore any attribute for any reason." If that is the case, can someone suggest a workaround? I would rather not do something as heavy-handed as disabling UBSan for the entire target.
GCC will emit the warning with a prepositional attribute, if you replace defaulting the destructor by an empty body. Postpositional attribute will not trigger warnings.
struct Foo {
[[deprecated,gnu::noinline,gnu::error("test")]]
~Foo() { __asm__(""); }
};
int main() {
Foo* f = new Foo();
delete f;
return 0;
}
This 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
The empty __asm__
prevents inlining, to ensure the gnu::error
diagnostic is triggered. Clang behaves similarly; going by the latter’s diagnostics, postpositional attributes are apparently applied to the type of this
(!). MSVC, on the other hand, always ignores the [[deprecated]]
attribute on destructors. (I’m not too surprised, given that it makes little sense to deprecate destructors. They may have never bothered to test this.)
I imagine that defaulting the destructor makes GCC and Clang ignore the destructor entirely and instead use built-in destruction logic to generate code instead. I haven’t yet consulted the standard to know how the specification plays into this.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With