6

Let's say I have the following code:

class Example
{
#ifndef PRIVATE_DESTRUCTOR
public:
#endif
    ~Example() { }
public:
    friend class Friend;
};

class Friend
{
public:
    void Member();
};

void Friend::Member()
{
    std::printf("Example's destructor is %s.\n",
        IsDestructorPrivate<Example>::value ? "private" : "public");
}

Is it possible to implement the IsDestructorPrivate template above to determine whether a class's destructor is private or protected?

In the cases I'm working with, the only times I need to use this IsDestructorPrivate are within places that have access to such a private destructor, if it exists. It doesn't necessarily exist. It is permissible for IsDestructorPrivate to be a macro rather than a template (or be a macro that resolves to a template). C++11 is fine.

Myria
  • 3,372
  • 1
  • 24
  • 42
  • why would you create private destructor? –  Aug 14 '14 at 20:50
  • 1
    AFAIK there is no way to check accessibility. And ideally you should not write code that depends on that. – Cheers and hth. - Alf Aug 14 '14 at 20:51
  • @mohaned for singleton-like behaviour maybe? – vsoftco Aug 14 '14 at 20:51
  • 2
    @mohaned here is a better answer: http://stackoverflow.com/q/631783/3093378 – vsoftco Aug 14 '14 at 20:53
  • @mohaned - `why would you create private destructor?` So that you can control how clients create/destroy instances of your class. – PaulMcKenzie Aug 14 '14 at 20:54
  • Sounds totally wrong. Why do you construct a class with a private destructor. Inhibit the construction in the first place. –  Aug 14 '14 at 20:55
  • http://stackoverflow.com/questions/631783/what-is-the-use-of-having-destructor-as-private (vsoftco beat me to it) – PaulMcKenzie Aug 14 '14 at 20:58
  • @DieterLücking: You probably would make the constructor private in that case, too. Of course *completely* inhibiting construction would probably make the class useless (as would completely inhibiting destruction), although there can be exceptions (classes which are only defined for the type). – celtschk Aug 14 '14 at 20:59

1 Answers1

10

You could use the std::is_destructible type trait like the example below:

#include <iostream>
#include <type_traits>

class Foo {
  ~Foo() {}
};

int main() {
  std::cout << std::boolalpha << std::is_destructible<Foo>::value << std::endl;
}

LIVE DEMO

std::is_destructible<T>::value will be equal to false if the destructor of T is deleted or private and true otherwise.

101010
  • 41,839
  • 11
  • 94
  • 168
  • Damn. That doesn't exist in Visual Studio 2010 and plain doesn't work in Visual Studio 2013. https://connect.microsoft.com/VisualStudio/feedback/details/811436/vc-is-destructible-doesnt-work – Myria Aug 15 '14 at 01:33
  • Also, the Standard seems unclear on whether std::is_destructible is, in fact, supposed to even return false for private/protected destructors. – Myria Aug 15 '14 at 01:34
  • @Myria as you've already discovered Visual studio is buggy on using `std::is_destructible` type trait. IMHO though, I think the standard is clear that `std::is_destructible::value` will return `false` if destructor is not accessible since it requires that *expression `std::declval().˜U()` is well-formed when treated as an unevaluated operand*, where in the case of `~U()` is inaccessible is not. Alternatively, in visual studio you could use boost to overcome this misfeature. – 101010 Aug 15 '14 at 02:07
  • Before posting this, I checked the Git repository copy of the standard; that wording was unclear. I didn't see anything specifying ''in which context'' the well-formedness of the expression is checked. `std::declval().~U()` is well-formed in `class U` or `friend` scope. For example, http://pastebin.com/j3BvWkcm – Myria Aug 15 '14 at 19:46
  • An expression is well defined if it doesn't make the program ill defined. To that end, if expression `std::declval().~U()` is evaluated in a context where destructor of `U` is accessible (e.g., a member function of `U`, a declared `friend` of `U` etc.) then it is well formed and is going to be evaluated to `void`. If it is evaluated in context where destructor of `U` is inaccessible expression is ill-formed because simply the program can't compile. Type-traits use SFINAE. That is, if `std::is_destructible::value` is evaluated in a context where `T`'s destructor's is inaccessible... – 101010 Aug 15 '14 at 23:51
  • ... only the trait that makes `std::is_destructible::value == false` is going to be instantiated because expression `std::declval().~U()` when destructor of `U` is inaccessible can't compile/evaluated. On the other hand when `std::is_destructible::value` is used in a context where destructor of `T` is accessible the type trait where `std::is_destructible::value == true` is going to be instantiated because expression `std::declval().~U()` can be evaluated to `void` it is compilable. HTH – 101010 Aug 15 '14 at 23:58
  • @Myria To clear things up more, try to put the expression `decltype(std::declval().~U()) *ptr = nulptr;` somewhere in `main`. You'll see that program doesn't compile due to this expression (i.e., `std::declval().~U()` is not well form thus it breaks the program). On the other hand if `U`'s destructor is `public` (i.e., accessible) it compiles fine. – 101010 Aug 16 '14 at 00:17
  • Let us [continue this discussion in chat](http://chat.stackoverflow.com/rooms/59443/discussion-between-myria-and-40two). – Myria Aug 16 '14 at 04:46