If-else depends on whether T is a full type

How to check if a specific type is a full type in a specific .cpp ?

 template<class T>class Test{ //some fields void(*functor)(T*) =[](T*){}; //^ will be written by some .cpp that can access T as complete-type T* t=nullptr; void fComplete(){ delete t; //faster /** ^ some code that use complete type*/ } void fForward(){ functor(t); //slower /** ^ some code that forward declaration is enough*/ } void f(){ /*if(T is complete type){ fComplete(); }else fForward();*/ } }; 

demo

This will be useful if I want to prematurely optimize the delete function in my custom smart pointer.

Can anyone confirm that this is not possible? I do not ask for a workaround (but I do not mind) - this question is just my curiosity.

+7
c ++ templates c ++ 14 incomplete-type
source share
2 answers

It works

 #include <iostream> #include <type_traits> using namespace std; class Incomplete; class Complete {}; template <typename IncompleteType, typename = std::enable_if_t<true>> struct DetermineComplete { static constexpr const bool value = false; }; template <typename IncompleteType> struct DetermineComplete< IncompleteType, std::enable_if_t<sizeof(IncompleteType) == sizeof(IncompleteType)>> { static constexpr const bool value = true; }; int main() { cout << DetermineComplete<Complete>::value << endl; cout << DetermineComplete<Incomplete>::value << endl; return 0; } 

Note I like to use std::enable_if_t for the same effect as void_t until it is available, instead of writing its implementation myself.

Note Look at the other answer as well as the ODR. They raise a valid point that you must consider before using this.

+7
source share

There, the rule in C ++ is called ODR. The very basics of this rule (from my understanding) are that something can have as many ads as you want, but only one definition. It seems simple, but with templates and a built-in function, it's pretty easy to break it.

Using templates, multiple definitions are inevitable. Ignoring the same template will occur in all translation units that use it. This is similar to one definition rule, but the rule has been extended for inline and template objects. Here is a paragraph on cppreference:

A program can have more than one definition if each definition appears in a different translation unit, each of the following: class type, enumeration type, built-in function with external binding of a built-in variable with external communication (starting with C ++ 17), class template, template non-static function, static data member of the class template, member function of the class template, partial specialization template, if everything is true:

  • each definition consists of the same sequence of tokens (usually appears in the same header file)

  • Searching for a name from each definition finds the same objects (after overload resolution), except that constants with internal ones or no binding can refer to different objects if they are not ODR - they are used and have the same values ​​in each definition.

  • Overloaded operators, including conversion, distribution, and deallocation functions, refer to the same function from each definition (unless you refer to the definition defined in the definition)

  • the language connection is the same (for example, the include file is not inside the extern "C" block)

  • the three rules above apply to each default argument used in each definition

  • if this definition refers to a class with an implicit constructor declaration, each translation unit where it is used as odr must call the same constructor for the base and members

  • if the definition is for the template, then all of these requirements apply to both names at the definition point and dependent names at the instantiation point

If all these requirements are met, the program behaves as if there is only one definition in the entire program. Otherwise, the behavior is undefined.

In short, if a function template expands to slightly different things in some translation units, you end up in UB land. Believe me, debugging an ODR violation is worst of all, because your program may run for a long time and suddenly crash when changing some compilation parameters, such as optimization.

In your particular case, you want to determine if the type is complete or not to change the definition of the function. Since in some places you may have a full type and creating this function, you will get a multiple and different definition of this function.

Be careful with macros. If some macro definition changes only in a certain translation, and you use this macro in a template or inline function, you violate ODR, since the function will not consist of the same tokens.


I now acknowledge that other answers are also helpful. Finding whether a type is complete is not entirely worthless. I use it in my code. I use it to provide good diagnostics with static_assert , which even some STL implementations do ( unique_ptr destructor in the GCC STL).

+2
source share

All Articles