14

During Andrei Alexandrescu's talk on error handling:

See C++ and Beyond 2012: Andrei Alexandrescu - Systematic Error Handling in C++ (about 30 minutes in)

Andrei presents the following piece of code:

~Expected()
{
    using std::exception_ptr;
    if (gotHam) ham.~T();
    else spam.~exception_ptr();
}

This destructor is cleaning up a union which contains either some type T or a std::exception_ptr. The union is populated using placement new.

Andrei then explains that the using std::exception_ptr; is necessary because the following code does not parse:

    else spam.~std::exception_ptr();

This means that it is always necessary to have a using directive if you need to explicitly call the destructor of a class in a different namespace.

Why doesn't the second example parse?

Would the followng code be a valid alternative?

    else delete spam;

Does this have the same affect as explicitly calling the destructor of std::exception_ptr

Gorpik
  • 10,940
  • 4
  • 36
  • 56
mark
  • 7,381
  • 5
  • 36
  • 61
  • 8
    Regarding the latter question, no. `delete` consists of calling the destructor *and* invoking `operator delete`. – R. Martinho Fernandes Jan 04 '13 at 11:51
  • I can see why it won't work with std:: but I'm surprised you need to bring it into the namespace. spam is already of type exception_ptr and you are just invoking the destructor for memory that was allocated with placement new? spam is presumably a reference not a pointer given you use . not -> but delete &spam invalid too if placement new was used. – CashCow Jan 04 '13 at 12:02
  • @R. Martinho Fernandes, thanks, this question offers some further explanation. http://stackoverflow.com/questions/6783993/placement-new-and-delete – mark Jan 04 '13 at 12:03
  • 1
    You can work it around with an aias template: `template using alias = T;` then write `spam.~alias()` – Johannes Schaub - litb Jan 04 '13 at 22:07

3 Answers3

10

Andrei probably uses using std::exception_ptr; because his compiler is broken.

There's no need. spam.~exception_ptr(); should compile just fine without it.

3.4.5/3. If the unqualified-id is ~type-name, the type-name is looked up in the context of the entire postfix-expression. If the type T of the object expression is of a class type C, the type-name is also looked up in the scope of class C.

It indeed compiles with gcc.

If you need to use qualified-name for some reason, spam.std::exception_ptr::~exception_ptr(); also compiles.

n. m. could be an AI
  • 112,515
  • 14
  • 128
  • 243
  • Ah, I had not read that clause in the standard. Andrei must have used Microsoft's compiler, which is the one I also used and behaves that way. – Gorpik Jan 04 '13 at 13:01
8

The problem here is that ~std::exception_ptr() is not really the name of the function you are trying to call, but just ~exception_ptr(). And, since it belongs to a class in a different namespace, it is inaccessible (EDIT: though it should be accessible according to §3.4.5/3 in the C++11 standard, as n.m. points out in his answer, but Microsoft compiler behaves this way).

You have an alternative to bringing the class into your namespace: do an explicit call using the qualified class name:

else spam.std::exception_ptr::~exception_ptr(); // This is legal

As for your second question, as R. Martinho Fernandes correctly explained in a comment, calling the delete operator is not equivalent to just calling the destructor: it also calls the awkwardly named function operator delete().

Gorpik
  • 10,940
  • 4
  • 36
  • 56
3

The syntax spam.~std::exception_ptr isn't allowed because the grammar ask for an id-expression and ~std::exception_ptr isn't one, as Gorpik pointed, you need spam.std::exception_ptr::~exception_ptr(). But I'm not understanding the reason why qualification is needed, in the clause describing the syntax, it is reminded that

because the name of a class is inserted in its class scope (Clause 9), the name of a class is also considered a nested member of that class.

so I think that spam.~exception_ptr() should be valid even without a using clause. BTW

namespace ns {
struct Foo {};
}

void f()
{
    ns::Foo x;
    x.~Foo();
}

compile cleanly with all g++ I've access to (included the very old 2.95). That seems to confirm my opinion that if it doesn't work in the context of the updated union types of C++11, it's a bug in the implementation.

Edit, with g++ 4.7.1, the following compiles as well with -std=c++11.

namespace ns {
struct Foo {};
}

struct Bar {};

union U {
    ns::Foo f;
    Bar b;
};

struct C {
    bool b;
    U u;
    ~C() {
        if (b)
            u.f.~Foo();
        else
            u.b.~Bar();
    }                
};

void f()
{
    C c;
}

so Andrei has be side-tracked (either by a bug in the compiler he was using or by forgetting the fact that class names are imported in the scope of a class) trying a problem which didn't need to be solved.

AProgrammer
  • 51,233
  • 8
  • 91
  • 143
  • VS2010 (the compiler I am using) works as explained in the question. Reading §12.4/12, I understand that the explicit call must use the type name (in this case, `std::exception_ptr`), but this should also be the name of the function called, which is not true. Anyway, this is not too clear for me: I don't know who is right in this case, VS (and Alexandrescu) or GCC. – Gorpik Jan 04 '13 at 12:55
  • Ah, reading n.m.'s answer, I had not looked at that clause in the standard. Now I think VS is wrong. – Gorpik Jan 04 '13 at 12:58