Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Non-trivial destructor make class non-trivially-constructible

Tags:

c++

destructor

Consider following code:

#include <type_traits>

struct T {};

static_assert(std::is_trivially_destructible< T >{});
static_assert(std::is_trivially_default_constructible< T >{});

struct N { ~N() { ; } };

static_assert(!std::is_trivially_destructible< N >{});
static_assert(!std::is_trivially_default_constructible< N >{});

It compiles fine using clang 3.7.0: live example. But summarizing the Standard:

The default constructor for class T is trivial (i.e. performs no action) if all of the following is true:

  • The constructor is not user-provided (i.e., is implicitly-defined or defaulted)
  • T has no virtual member functions
  • T has no virtual base classes
  • T has no non-static members with default initializers. (since C++11)
  • Every direct base of T has a trivial default constructor
  • Every non-static member of class type has a trivial default constructor

As I can see there is no dependence on triviality of the destructor.

I missed something? Is it clang bug?

ADDITIONAL

I found a workaround: is static_assert(__has_trivial_constructor( N )); built-in type trait. There is support in clang, gcc and MSVC.

For is_noexcept_constructible family of type traits there is workaround too.

like image 393
Tomilov Anatoliy Avatar asked Nov 25 '15 05:11

Tomilov Anatoliy


People also ask

Is there a non-trivial destructor?

A class has a non-trivial destructor if it either has an explicitly defined destructor, or if it has a member object or a base class that has a non-trivial destructor.

What is non-trivial constructor in C++?

If you define a constructor yourself, it is considered non-trivial, even if it doesn't do anything, so a trivial constructor must be implicitly defined by the compiler.


1 Answers

This issue is covered in LWG issue 2116: std::swap noexcept(what?), we can see this from the cppreference section for std::is_trivially_default_constructible:

In many implementations, is_nothrow_default_constructible also checks if the destructor throws because it is effectively noexept(T()): GCC bug 51452 LWG issue 2116

which deceptively only talks about is_nothrow_default_constructible but if we read the issue in detail we see it also applies here as well.

Perhaps is is easier if we follow the gcc bug report: [DR 2116] has_nothrow_.*constructor bugs referenced first which says:

The traits that detect nothrow constructibility are buggy because they are influenced by whether the object has a nothrow dtor; destruction is invoked at the end of evaluation of the full expression in the noexcept( ... ) operator. They all use the pattern of constructing a temporary inside noexcept, whereas they should be using placement new

this explicitly says what is only really alluded to in the LWG issue which eventually says:

is_nothrow_constructible is defined in terms of is_constructible, which is defined by looking at a hypothetical variable and asking whether the variable definition is known not to throw exceptions. The issue claims that this also examines the type's destructor, given the context, and thus will return false if the destructor can potentially throw. At least one implementation (Howard's) does return false if the constructor is noexcept(true) and the destructor is noexcept(false). So that's not a strained interpretation. The issue is asking for this to be defined in terms of placement new, instead of in terms of a temporary object, to make it clearer that is_nothrow_constructible looks at the noexcept status of only the constructor, and not the destructor.

which also effects std::is_trivially_default_constructible which relies on std::is_trivially_constructible which does the same as is_constructible but has the further restriction that:

but the variable definition does not call any operation that is not trivial

like image 133
Shafik Yaghmour Avatar answered Oct 06 '22 00:10

Shafik Yaghmour