Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

if-else depends on whether T is a complete type

How to check whether a certain type is a complete type in a certain .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

It will be useful when I want to prematurely optimize a delete function in my custom smart pointer.

Can anyone confirm that it is impossible?
I am not asking for a workaround (but I don't mind) - this question is just my curiosity.

like image 772
javaLover Avatar asked Jun 03 '17 03:06

javaLover


People also ask

What are the three types of IF statement?

There are three forms of IF statements: IF-THEN , IF-THEN-ELSE , and IF-THEN-ELSIF . The simplest form of IF statement associates a Boolean expression with a sequence of statements enclosed by the keywords THEN and END IF . The sequence of statements is executed only if the expression returns TRUE .

What is the if else conditional structure?

If/Else - A common form of conditional statements in programming; tells the computer that if the condition is true, do this. Else, if the condition is false, do another thing.

Why is my else if statement not working Java?

If you are getting an error about the else it is because you've told the interpreter that the ; was the end of your if statement so when it finds the else a few lines later it starts complaining. A few examples of where not to put a semicolon: if (age < 18); if ( 9 > 10 ); if ("Yogi Bear". length < 3); if ("Jon".

What are if else statements?

An if else statement in programming is a conditional statement that runs a different set of statements depending on whether an expression is true or false.


2 Answers

This 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 that is available instead of writing its implementation myself everywhere.

Note Do take a look at the other answer as well about ODR. They bring up a valid point that you should consider before using this.

like image 123
Curious Avatar answered Oct 16 '22 18:10

Curious


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

With templates, multiple definition is inevitable. Instantiation of the same template will happen in all translation unit that uses it. It seems against the one definition rule, but for inline and templated entities, the rule is extended. Here's a paragraph on cppreference:

There can be more than one definition in a program, as long as each definition appears in a different translation unit, of each of the following: class type, enumeration type, inline function with external linkage inline variable with external linkage (since C++17), class template, non-static function template, static data member of a class template, member function of a class template, partial template specialization, as long as all of the following is true:

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

  • name lookup from within each definition finds the same entities (after overload-resolution), except that constants with internal or no linkage may refer to different objects as long as they are not ODR-used and have the same values in every definition.

  • overloaded operators, including conversion, allocation, and deallocation functions refer to the same function from each
    definition (unless referring to one defined within the definition)

  • the language linkage is the same (e.g. the include file isn't inside an extern "C" block)

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

  • if the definition is for a class with an implicitly-declared constructor, every translation unit where it is odr-used must call the same constructor for the base and members

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

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

In short, if any function template expands to slightly different things in some translation units, you end up in the UB land. Trust me, debugging ODR violation is the worst, because your program might work for a long time, and suddenly crash when changing some compilation options, like optimisations.

In your particular case, you want to detect if a type is complete or not to change the definition of a function. Since in some places you might have a complete type and instantiate that function, you'll end up with multiple and different definition of that function.

Be careful with macros too. If some macro definition changes in only some translation and you use that macro in a template or inline function, you violate ODR, since the function won't consist of the exact same tokens.


Now, I acknowledge that other answers also are useful indeed. Detecting whether a type is complete is not entirely useless. I use it in my code. I use it to provide nice diagnostics with static_assert, which even some implementations of the STL do (unique_ptr destructor in GCC's STL).

like image 30
Guillaume Racicot Avatar answered Oct 16 '22 16:10

Guillaume Racicot