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
Are Static Variables in a Base Class Shared by All Derived Classes
A C++ Implementation That Detects Undefined Behavior
Fast Cross-Platform C/C++ Image Processing Libraries
Narrowing Conversions in C++0X. Is It Just Me, or Does This Sound Like a Breaking Change
Are There Practical Uses for Dynamic-Casting to Void Pointer
Setupdigetdeviceproperty Usage Example
Set Precision of Std::To_String When Converting Floating Point Values
Getline Not Working Properly? What Could Be the Reasons
Why Is "Using Namespace X;" Not Allowed at Class/Struct Level
What Is the Default Value for C++ Class Members
Compiling a Static Executable with Cmake
Compile Time Sizeof_Array Without Using a MACro
Difference Between <String> and <String.H>
How Is C++ Std::Vector Implemented
Tmp: How to Generalize a Cartesian Product of Vectors