Destructors of Builtin Types (Int, Char etc..)

Destructors of builtin types (int, char etc..)

It's the reason that makes your code work for generic parameters. Consider a container C:

template<typename T>
struct C {
// ...
~C() {
for(size_t i = 0; i<elements; i++)
buffer[i].~T();
}
};

It would be annoying to introduce special cases for built-in types. So C++ allows you to do the above, even if T happens to equal to int. The holy Standard says in 12.4 p15:

The notation for explicit call of a destructor can be used for any scalar type name. Allowing this makes it possible to write code without having to know if a destructor exists for a given type.

The difference between using a plain int and a typedef'ed int is that they are syntactically different things. The rule is, that in a destructor call, the thing after the ~ is a type-name. int is not such a thing, but a typedef-name is. Look it up in 7.1.5.2.

Is it legal to call a destructor on int32_t?

There's a clear requirement that int32_t be a typedef. We start with [cstdint.syn]/2:

The header defines all functions, types, and macros the same as 7.18 in the C standard.

So from there we look at the requirement for the C library:

The typedef name intN_t designates a signed integer type with width N, no padding bits, and a two’s complement representation.

[emphasis added]

So yes, int32_t must be a "typedef name".

Although (as far as I know) it's never stated directly in normative text, the following note makes it clear that invoking a destructor for a typedef that resolves to a built-in type is intended to compile and succeed ( [class.dtor]/16):

Note: the notation for explicit call of a destructor can be used for any scalar type name (5.2.4). Allowing this makes it possible to write code without having to know if a destructor exists for a given type. For example,

typedef int I;
I* p;
p->I::~I();

Prospective destructors in C++

That's indeed a reported Clang bug1, as noted by Quimby.

Note that the second snippet (the one with the the constrained destructor first) doesn't really "work" in Clang, which just ignores the second destructor2.

Also note that, unlike gcc, at the moment I'm writing, Clang doesn't seem to have implemented [P0848R3] (which is about conditional trivial special member functions) yet3.


1) https://bugs.llvm.org/show_bug.cgi?id=50570

2) See e.g.: https://godbolt.org/z/rff7qfK65

3) See the reported values of the feature test macro __cpp_concepts, e.g. here: https://godbolt.org/z/P4z3Pj5vT

Emplace with primitive types

The parameters in the shown code are all lvalues.

For the emplaced primitive type, as is the case here: an lvalue that gets passed to emplace() does not get modified. If the container contains a class with a constructor, that emplace ends up invoking, for a "well-behaved" constructor the parameter won't get modified whether it's an int or a discrete class instance.

The only time something that gets passed to emplace() typically gets altered, in some way, would be if that "something" is a movable rvalue, and the corresponding constructor parameter is an rvalue, and the constructor moves the rvalue somewhere else, leaving the original parameter in some valid, but unspecified state. Or, if "something" is a non-const lvalue, and the constructor intentionally modifies it, but that would be rather rude (not well-behaved).

This wouldn't be the case for primitive types, in any case.

C++ type-based dispatch for builtin types

If you have compiler with C++17 support this snippet of code should work:

template<typename T>
T dispatcher() {
// if T is double
if constexpr (std::is_same<T, double>::value)
return _func_double();
// if T is int
if constexpr (std::is_same<T, int>::value)
return _func_int();
// if T is char*
if constexpr (std::is_same<T, char*>::value)
return _func_char_pointer();
}

Otherwise you will have to do template specialization, and make overload for each of parameters that you want

//only needed for static assert
template<typename T>
struct always_false : std::false_type {};

template<typename T>
T dispatcher()
{
//to make sure that on type you didn't overload you will have exception
throw std::exception("This type was not overloaded")
//static assert that will provide compile time error
static_assert(always_false<T>::value , "You must specialize dispatcher for your type");
}
//or to get compile time error without static assert
template<typename T>
T dispatcher() = delete; //the simplest solution

template<>
double dispatcher<double>()
{
return _func_double();
}
//... and so on for all other functions

Using builtin types (double, int, etc.) in a C++ namespace?

int and friends are keywords in the language. They are not names in the global namespace; they are not names at all as far as the language is concerned. Qualifying them with :: is an error. Trying to name any variable int is also an error. Specifically, see 2.1 paragraph 1 in C++03, and 2.12 paragraph 1 in C++11 (the text is the same):

The identifiers shown in Table 3 are reserved for use as keywords (that is, they
are unconditionally treated as keywords in phase 7):
[ ... ]
int


Related Topics



Leave a reply



Submit