I think the reason why accessing non-static data members in a function-try-block of a destructor is that [except.ctor]/2 and [except.handle]/11 guarantee that all subobjects have already been destroyed when entering the catch-clause of said try-block:
github draft from 2014-07-23, [except.ctor]/2
An object of any storage duration whose initialization or destruction
is terminated by an exception will have destructors executed for all
of its fully constructed subobjects (excluding the variant members of
a union-like class), that is, for subobjects for which the principal
constructor has completed execution and the destructor has not yet
begin execution.
[except.handle]/11
[...] The base classes and non-variant members of
an object shall be destroyed before entering the handler of a
function-try-block of a destructor for that object.
Therefore, whether we throw the exception in the dtor of the class itself, or in the dtor of a subobject: all subobjects will be destroyed.
Example 1:
#include <iostream>
struct loud
{
~loud() { std::cout << "~loud()\n"; }
};
struct T
{
loud l;
~T() noexcept(false)
try
{
std::cout << "throwing an int\n";
throw 42;
}catch(int)
{
std::cout << "caught an int\n";
throw;
}
};
int main()
{
try
{
T t;
}catch(...)
{
std::cout << "caught an exception in main\n";
}
}
Output:
throwing an int
~loud()
caught an int
caught an exception in main
Live example
Example 2:
#include <iostream>
struct loud
{
loud() { std::cout << "loud()\n"; }
~loud() { std::cout << "~loud()\n"; }
};
struct A
{
A() { std::cout << "A()\n"; }
~A() noexcept(false) { std::cout << "~A()\n"; throw 42; }
};
struct T
{
loud l;
A a;
~T() noexcept(false)
try
{
}catch(int)
{
std::cout << "caught an int\n";
throw;
}
};
int main()
{
try
{
T t;
}catch(...)
{
std::cout << "caught an exception in main\n";
}
}
Output:
loud()
A()
~A()
~loud()
caught an int
caught an exception in main
Live example