Khurshid Normuradov
Khurshid Normuradov

Reputation: 906

template class destructor for non-trivial types

I'm playing with c++20 concepts

https://godbolt.org/z/931xaeY45

#include <type_traits>

template<typename T>
class Optional
{
public:
    ~Optional() requires (!std::is_trivially_destructible_v<T>);

};

#include <cstdio>
int main()
{
    const char* bool_value[] = {"false", "true"};
    
    bool is_trivial = std::is_trivial_v<Optional<int>>;
    bool is_trivial_destructable = std::is_trivially_destructible_v<Optional<int>>;
    
    std::printf("std::is_trivial_v<Optional<int>> = %s\n", bool_value[is_trivial]);
    std::printf("std::is_trivial_destructable<Optional<int>> = %s\n", bool_value[is_trivial_destructable]);
}

Output:

std::is_trivial_v<Optional<int>> = false
std::is_trivial_destructable<Optional<int>> = false

But, I expected that the destructor doesn't get instantiated, because for this situation requires (false) is generated for template parameter int.

Q: Why does ~Optional() requires (false) make class Optional non-trivial?

Upvotes: 2

Views: 359

Answers (2)

Nicol Bolas
Nicol Bolas

Reputation: 473397

My read of the standard, and MSVC, says that this is a compile error.

In C++20, this:

~Optional() requires (!std::is_trivially_destructible_v<T>);

is a "prospective destructor". It may or may not become the actual destructor based on a series of rules. But what really matters is this rule:

If a class has no user-declared prospective destructor, a prospective destructor is implicitly declared as defaulted ([dcl.fct.def]).

Your class has a "user-declared prospective destructor". Therefore, there is no "implicitly declared as defaulted" constructor ever.

That's important because this rule kicks in:

At the end of the definition of a class, overload resolution is performed among the prospective destructors declared in that class with an empty argument list to select the destructor for the class, also known as the selected destructor. The program is ill-formed if overload resolution fails.

Since T is trivially destructible, overload resolution can't call your prospective desturctor, since the requires constraint kills it. And there are no other destructors to find. So it isn't supposed to compile, and GCC may simply not yet implement the rule correctly.

Regardless, you need to provide a defaulted alternative destructor:

~Optional() requires (!std::is_trivially_destructible_v<T>);
~Optional() = default;

Upvotes: 3

2b-t
2b-t

Reputation: 2564

From my understanding you have to supply an alternative for trivially destructible types T with default (seems to work in GCC)

template<typename T>
class Optional {
public:
  // Chosen for not trivially destructible types
  ~Optional() requires (not std::is_trivially_destructible_v<T>);
  // Chosen for everything else (trivially destructible types)
  ~Optional() = default;
};

In your case there is only one option given that is not set to default which makes it not default destructible.

Upvotes: 1

Related Questions