2

I am trying to specify that a function is nothrow whenever the destructor of Foo doesn't throw. I can do this by using the type trait std::is_nothrow_destructible<>. How can I do this directly? I've tried the following, but it doesn't compile if I uncomment the commented line

#include <iostream>
#include <type_traits>

class Foo
{
public:
    ~Foo() noexcept {}
};

// void f() noexcept(noexcept(~Foo{})) { } // error here
void g() noexcept(std::is_nothrow_destructible<Foo>::value) 
{

}

int main()
{
    g();
}

I get an error

 error: no match for 'operator~' (operand type is 'Foo')

The error specifier noexcept(noexcept(~Foo())) is not OK, although for constructors I can use noexcept(noexcept(Foo())). Am I missing some obvious syntax here?

vsoftco
  • 55,410
  • 12
  • 139
  • 252

1 Answers1

5

Destructors can only be called through a member access expression. So the syntax would be:

void f() noexcept(noexcept(std::declval<Foo>().~Foo()))
David G
  • 94,763
  • 41
  • 167
  • 253
  • Perfect! It works. Why is this asymmetry between constructors and destructors? Is it because in principle you don't "manually" invoke the destructor? – vsoftco Feb 19 '15 at 01:50
  • 1
    The asymmetry is because for `noexcept(expr)`, `expr` has to be a valid expression. `Foo()` is a valid expression that could occur outside of a `noexcept` specifier, but `~Foo()` is not. You can, however, write `Foo f; f.~Foo()`, which is effectively what this answer does. – Tavian Barnes Feb 19 '15 at 01:53
  • 1
    @TavianBarnes the `declval` doesn't create any temporary though, it uses an unevaluated expression. – vsoftco Feb 19 '15 at 01:55
  • @vsoftco I believe it's because the syntax `~Foo()` is ambiguous between a destructor call and a user-defined `operator~()` being called on a temporary `Foo` instance. – David G Feb 19 '15 at 01:56
  • 2
    @0x499602D2 The bigger problem is "what is `~Foo()` supposed to destroy?" – T.C. Feb 19 '15 at 01:59
  • 2
    @vsoftco A constructor creates a new instance, a destructor must be invoked on an existing instance. The `declval` expression provides this instance (without actually creating one). – Praetorian Feb 19 '15 at 02:00
  • @T.C. That's a better way of phrasing it. Destructors are not any different than other member functions in this sense. – Tavian Barnes Feb 19 '15 at 02:13
  • 2
    @0x499602D2: I don't think it's *ambiguous*... `~Foo()` is unambiguously negation of a default-constructed `Foo`, as the destructor's only considered when there's member-function call syntax ala `a_foo.~Foo()`. – Tony Delroy Feb 19 '15 at 02:19
  • @TonyD He's asking why there's a difference between a constructor and destructor call. If a destructor could be called like `~Foo()` then it would be ambiguous with the negation syntax. T.C. gave a good reason why it isn't allowed. – David G Feb 19 '15 at 02:24