What is the difference between 'typedef' and 'using' in C++11?

后端 未结 7 810
误落风尘
误落风尘 2020-11-22 03:32

I know that in C++11 we can now use using to write type alias, like typedefs:

typedef int MyInt;

Is, from what I

相关标签:
7条回答
  • 2020-11-22 03:54

    They are largely the same, except that:

    The alias declaration is compatible with templates, whereas the C style typedef is not.

    0 讨论(0)
  • 2020-11-22 03:56

    They are essentially the same but using provides alias templates which is quite useful. One good example I could find is as follows:

    namespace std {
     template<typename T> using add_const_t = typename add_const<T>::type;
    }
    

    So, we can use std::add_const_t<T> instead of typename std::add_const<T>::type

    0 讨论(0)
  • 2020-11-22 03:56

    I know the original poster has a great answer, but for anyone stumbling on this thread like I have there's an important note from the proposal that I think adds something of value to the discussion here, particularly to concerns in the comments about if the typedef keyword is going to be marked as deprecated in the future, or removed for being redundant/old:

    It has been suggested to (re)use the keyword typedef ... to introduce template aliases:

    template<class T>
      typedef std::vector<T, MyAllocator<T> > Vec;
    

    That notation has the advantage of using a keyword already known to introduce a type alias. However, it also displays several disavantages [sic] among which the confusion of using a keyword known to introduce an alias for a type-name in a context where the alias does not designate a type, but a template; Vec is not an alias for a type, and should not be taken for a typedef-name. The name Vec is a name for the family std::vector<•, MyAllocator<•> > – where the bullet is a placeholder for a type-name.Consequently we do not propose the “typedef” syntax.On the other hand the sentence

    template<class T>
      using Vec = std::vector<T, MyAllocator<T> >;
    

    can be read/interpreted as: from now on, I’ll be using Vec<T> as a synonym for std::vector<T, MyAllocator<T> >. With that reading, the new syntax for aliasing seems reasonably logical.

    To me, this implies continued support for the typedef keyword in C++ because it can still make code more readable and understandable.

    Updating the using keyword was specifically for templates, and (as was pointed out in the accepted answer) when you are working with non-templates using and typedef are mechanically identical, so the choice is totally up to the programmer on the grounds of readability and communication of intent.

    0 讨论(0)
  • 2020-11-22 04:09

    They are equivalent, from the standard (emphasis mine) (7.1.3.2):

    A typedef-name can also be introduced by an alias-declaration. The identifier following the using keyword becomes a typedef-name and the optional attribute-specifier-seq following the identifier appertains to that typedef-name. It has the same semantics as if it were introduced by the typedef specifier. In particular, it does not define a new type and it shall not appear in the type-id.

    0 讨论(0)
  • 2020-11-22 04:09

    The using syntax has an advantage when used within templates. If you need the type abstraction, but also need to keep template parameter to be possible to be specified in future. You should write something like this.

    template <typename T> struct whatever {};
    
    template <typename T> struct rebind
    {
      typedef whatever<T> type; // to make it possible to substitue the whatever in future.
    };
    
    rebind<int>::type variable;
    
    template <typename U> struct bar { typename rebind<U>::type _var_member; }
    

    But using syntax simplifies this use case.

    template <typename T> using my_type = whatever<T>;
    
    my_type<int> variable;
    template <typename U> struct baz { my_type<U> _var_member; }
    
    0 讨论(0)
  • 2020-11-22 04:20

    All standard references below refers to N4659: March 2017 post-Kona working draft/C++17 DIS.


    Typedef declarations can, whereas alias declarations cannot, be used as initialization statements

    But, with the first two non-template examples, are there any other subtle differences in the standard?

    • Differences in semantics: none.
    • Differences in allowed contexts: some(1).

    (1) In addition to the examples of alias templates, which has already been mentioned in the original post.

    Same semantics

    As governed by [dcl.typedef]/2 [extract, emphasis mine]

    [dcl.typedef]/2 A typedef-name can also be introduced by an alias-declaration. The identifier following the using keyword becomes a typedef-name and the optional attribute-specifier-seq following the identifier appertains to that typedef-name. Such a typedef-name has the same semantics as if it were introduced by the typedef specifier. [...]

    a typedef-name introduced by an alias-declaration has the same semantics as if it were introduced by the typedef declaration.

    Subtle difference in allowed contexts

    However, this does not imply that the two variations have the same restrictions w.r.t. in which contexts they may be used. And indeed, albeit a corner case, a typedef declaration is an init-statement and may thus be used in contexts which allows initialization statements

    // C++11 (C++03) (init. statement in for loop iteration statements).
    for(typedef int Foo; Foo{} != 0;) {}
    
    // C++17 (if and switch initialization statements).
    if (typedef int Foo; true) { (void)Foo{}; }
    //  ^^^^^^^^^^^^^^^ init-statement
    
    switch(typedef int Foo; 0) { case 0: (void)Foo{}; }
    //     ^^^^^^^^^^^^^^^ init-statement
    
    // C++20 (range-based for loop initialization statements).
    std::vector<int> v{1, 2, 3};
    for(typedef int Foo; Foo f : v) { (void)f; }
    //  ^^^^^^^^^^^^^^^ init-statement
    
    for(typedef struct { int x; int y;} P;
    //  ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ init-statement
        auto [x, y] : {P{1, 1}, {1, 2}, {3, 5}}) { (void)x; (void)y; }
    

    whereas an alias-declaration is not an init-statement, and may thus not be used in contexts which allows initialization statements

    // C++ 11.
    for(using Foo = int; Foo{} != 0;) {}
    //  ^^^^^^^^^^^^^^^ error: expected expression
    
    // C++17 (initialization expressions in switch and if statements).
    if (using Foo = int; true) { (void)Foo{}; }
    //  ^^^^^^^^^^^^^^^ error: expected expression
    
    switch(using Foo = int; 0) { case 0: (void)Foo{}; }
    //     ^^^^^^^^^^^^^^^ error: expected expression
    
    // C++20 (range-based for loop initialization statements).
    std::vector<int> v{1, 2, 3};
    for(using Foo = int; Foo f : v) { (void)f; }
    //  ^^^^^^^^^^^^^^^ error: expected expression
    
    0 讨论(0)
提交回复
热议问题