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?
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.
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.
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.
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.
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 variablet
:T t(create<Args>()...);
Access checking is performed as if in a context unrelated to
T
and any of theArgs
. 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).
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 variablet
:T t(create<Args>()...);
In other words, is_constructible<T, Args...>::value
yields false
if the destructor is not accessible.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With