Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

std::is_constructible on type with non-public destructor

What is the expected result for std::is_constructible on a type with a private or protected destructor?

For instance, I can still construct such an object on the heap even though only a friend can free it:

#include <type_traits>

class Foo
{
    friend void freeFoo(Foo*);
public:
    Foo()
    {}
private:
    // Destructor is private!
    ~Foo()
    {}
};

void freeFoo(Foo* f)
{
    delete f;  // deleting a foo is fine here because of friendship
}

int main()
{
    Foo* f = new Foo();
    // delete f;   // won't compile: ~Foo is private
    freeFoo(f);    // fine because of friendship


    if(!std::is_constructible<Foo>::value)
    {
        std::cout << "is_constructible failed" << std::endl;
    }
}

The final check for is_constructible will fail on both gcc and Visual C++ (gcc demo on coliru).

Is that the required behavior by the standard? If so, is there any way to check whether the type has a specific constructor, regardless of the access specifier on the destructor?

like image 700
ComicSansMS Avatar asked Jan 22 '15 10:01

ComicSansMS


People also ask

How to check if a class is destructible in C++ STL?

The std::is_destructible template of C++ STL is used to check whether the T is destructible or not. A class is called destructible whose destructor is not deleted and potentially accessible in derived classes. It return the boolean value true if T is destructible type, otherwise return false.

What is is_constructible in C++?

Args> struct is_constructible; For this class, a constructible type is a type that can be constructed using a particular set of arguments. is_constructible inherits from integral_constant as being either true_type or false_type, depending on whether T is constructible with the list of arguments Args.

What is a destructible class in Java?

A class is called destructible whose destructor is not deleted and potentially accessible in derived classes. It return the boolean value true if T is destructible type, otherwise return false.

What is a constructible type in Java?

For this class, a constructible type is a type that can be constructed using a particular set of arguments. is_constructible inherits from integral_constant as being either true_type or false_type, depending on whether T is constructible with the list of arguments Args. A complete type, or void (possible cv-qualified), or an array of unknown bound.


2 Answers

The C++14 FD defines is_constructible as follows:

Given the following function declaration:

template <class T>
add_rvalue_reference_t<T> create() noexcept;

the predicate condition for a template specialization is_constructible<T, Args...> shall be satisfied if and only if the following variable definition would be well-formed for some invented variable t:

T t(create<Args>()...);

Access checking is performed as if in a context unrelated to T and any of the Args. Only the validity of the immediate context of the variable initialization is considered. [ Note: The evaluation of the initialization can result in side effects such as the instantiation of class template specializations and function template specializations, the generation of implicitly-defined functions, and so on. Such side effects are not in the “immediate context” and can result in the program being ill-formed. —end note ]

Now the question essentially reduces to "Is the destructor call in the immediate context of the variable initialization?" [class.dtor]/11:

A destructor is invoked implicitly

  • for a constructed object with static storage duration (3.7.1) at program termination (3.6.3),
  • for a constructed object with automatic storage duration (3.7.3) when the block in which an object is created exits (6.7),
  • for a constructed temporary object when its lifetime ends (12.2).

In each case, the context of the invocation is the context of the construction of the object.

Thus the destructor invocation is in the context of the construction (which is presumably synonymous to initialization here), which implies that it is considered and causes the trait to return false.
I believe this to be underspecified (e.g. immediate vs not-explicitly-immediate context?), but intuitively I expect a conforming implementation to mark the expression NotDestructible() as ill-formed - either SFINAE-friendly or not (preferably the former). Never well-formed, though.
Clang with libc++, libstdc++ and GCC do say that it's invalid, SFINAE-friendly.


If so, is there any way to check whether the type has a specific constructor, regardless of the access specifier on the destructor?

What about using new?

template <typename T, typename... Args>
class is_only_constructible
{
    template <typename, typename=void> struct test : std::false_type {};
    template <typename U>
    struct test<U, decltype(void(new U(std::declval<Args>()...)))> : std::true_type {};

public:
    static constexpr bool value = test<T>::value;
};

Demo. Consistent traits can be easily established: Take the is_only_constructible trait and combine it with is_destructible (clearly the latter returns false when combined with private destructors).

like image 175
Columbo Avatar answered Oct 21 '22 04:10

Columbo


Quoting paragraph [meta.unary.prop]/7 of the C++ Standard (Draft N4296):

Given the following function declaration:

template <class T>
add_rvalue_reference_t<T> create() noexcept;

the predicate condition for a template specialization is_constructible<T, Args...> shall be satisfied if and only if the following variable definition would be well-formed for some invented variable t:

T t(create<Args>()...);

In other words, is_constructible<T, Args...>::value yields false if the destructor is not accessible.

like image 22
Andy Prowl Avatar answered Oct 21 '22 02:10

Andy Prowl