Search code examples
c++preprocessorusing

Is there a difference between 'using' and '#define' when declaring a type alias?


I've known about using preprocessor instructions to shorten type names similar to this:

#define LL long long

However, I've seen someone use a different approach:

using LL = long long;

Are there any differences between these two, apart from the syntax? Which one would be more recommended in most cases?


Solution

  • using (or typedef before C++11) is the preferred approach. It creates a proper type alias that is actually treated by the compiler as a type and as nothing else.

    One very important reason NOT to use #define in this manner is that it is handled globally by the preprocessor before the compiler is invoked. It doesn't understand or respect C++ syntax. It is just arbitrary text replacement, and it will unconditionally replace all occurrences of the defined symbol with the specified text, regardless of where the symbol appears in the code.

    So, for example, if you have a function, variable, class type, basically any identifier named LL, the #define you have shown will replace it with long long, corrupting the code's syntax, eg:

    #define LL long long
    
    void Func1(LL value); // OK: compiler sees "void Func1(long long value);"
    void Func2(char LL); // ERROR: compiler sees "void Func2(char long long);"
    void LL(arguments...); // ERROR: compiler sees "void long long(arguments...);"
    
    class LL // ERROR: compiler sees "class long long"
    {
        LL value; // OK: compiler sees "long long value;"
        void Method1(LL value); // OK: compiler sees "void Method1(long long value);"
        void Method2(char LL); // ERROR: compiler sees "void Method2(char long long);"
        void LL(arguments...); // ERROR: compiler sees "void long long(arguments...);"
    };
    

    Whereas a using (or typedef) type alias will not corrupt the code's syntax like that:

    using LL = long long; // or: typedef long long LL;
    
    void Func1(LL value); // OK: compiler sees "void Func1(long long value);"
    void Func2(char LL); // OK: compiler sees "void Func2(char LL);"
    void LL(arguments...); // OK: compiler sees "void LL(arguments...);"
    
    class LL // OK: compiler sees "class LL"
    {
        LL value; // OK: compiler sees "long long value;"
        void Method1(LL value); // OK: compiler sees "void Method1(long long value);"
        void Method2(char LL); // OK: compiler sees "void Method2(char LL);"
        void LL(arguments...); // OK: compiler sees "void LL(arguments...);"
    };