The relationship between auto and decltype

后端 未结 5 2117
礼貌的吻别
礼貌的吻别 2020-12-12 17:35

Is

auto x = initializer;

equivalent to

decltype(initializer) x = initializer;

or

decltype         


        
5条回答
  •  予麋鹿
    予麋鹿 (楼主)
    2020-12-12 17:58

    auto

    auto is simple: it will give the same type as by-value template parameter deduction. auto works uniformly on expressions.

    template 
    void deduce(T x);
    
    int &refint();
    std::string str();
    std::string const conststr();
    
    auto i1 = 1; // deduce(1) gives T=int so int i1
    auto i2 = i1; // deduce(i1) gives T=int so int i2
    auto i3 = refint(); // deduce(refint()) gives T=int so int i3
    const auto ci1 = i1; // deduce(i1) gives T=int so const int ci1
    auto i4 = ci1; // deduce(ci1) gives T=int so int i4
    
    auto s1 = std::string(); // std::string s1
    auto s2 = str(); // std::string s2
    auto s3 = conststr(); // std::string s3
    

    In C++ expressions cannot have reference type (refint() has type int not int&).

    Note that the lvalueness of the expression is not an issue on an expression on the right (on the right of equal sign, or something which is being copied in general). The rvalue 1 is treated like the lvalues i1 and refint().

    For by value parameters (that is, non reference parameters), not only lvalue to rvalue conversion is applied, but also array to pointer conversions. const is ignored.

    decltype

    decltype is a very useful feature with an horrible interface:

    decltype acts differently on some expressions defined in term of name lookup and other expressions! This is the type of features that make people hate C++.

    decltype of something named

    decltype(entity) will do name lookup and give the declared type of the entity. (entity can be an unqualified or qualified identifier, or a member access such as expr.identifier.)

    decltype(f(args)) will do name lookup and overload resolution and give the declared return type of the function, not the type the expression:

    extern decltype(refint()) ri1; // int &ri1
    

    So now I can check my understanding of the language with decltype:

    template 
    struct sametype {};
    
    template 
    struct sametype {typedef int same;};
    

    sametype::same exists iff T and U are exactly the same type.

    sametype::same check_i1;
    sametype::same check_i2;
    sametype::same check_i3;
    sametype::same check_i4;
    
    sametype::same check_ci1;
    sametype::same check_ir1;
    
    sametype::same check_s1;
    sametype::same check_s2;
    sametype::same check_s3;
    

    compiles fine, so I was not wrong!

    decltype of other expressions

    Otherwise, where expr is not defined in term of name lookup (not one of the above cases), such as the expression (expr), decltype implements a distinct feature (but the C++ designers would not spent another keywords on it.)

    decltype(expr) will give the type of the expression decorated with its lxrvalueness (lvalue/xvalue/prvalue-ness):

    • prvalue (pure rvalue) of type T gives T
    • xvalue of type T gives T&&
    • lvalue of type T gives T&

    This is the reciprocal of function call rule: if f is a function with return type

    • T&, the expression f() is an lvalue
    • T&&, the expression f() is an xvalue
    • naked type (pure object type, non reference) T, the expression f() is a prvalue

    and also for casts: for naked type (pure object type, non reference) T

    • (T&)expr is a lvalue
    • (T&&)expr is an xvalue
    • (T)expr is a prvalue

    Reference-ness is the encoding of lxrvalueness into types. decltype does this encoding to save and transfer the lxrvalueness of things.

    This is useful when you want to alias an expression: the lxrvalueness of an expression expr that is a function call (either normal f(args) or with operator syntax like a @ b) is the same as the lxrvalueness of alias() declared as decltype(expr) alias();

    This can be used for pure forwarding in generic code:

    // decorated type of an expression
    #define EXPR_DEC_TYPE(expr) decltype((expr))
    
    int i;
    int &ri = i;
    int fi();
    int &fri();
    
    EXPR_DEC_TYPE(i) alias_i = i; // int &
    EXPR_DEC_TYPE(ri) alias_ri = ri; // int &
    
    EXPR_DEC_TYPE(fi()) alias_fi(); // int alias_fi()
    EXPR_DEC_TYPE(fri()) alias_fri(); // int &alias_fri()
    

    Note that EXPR_DEC_TYPE(foo()) equals declexpr(foo()) by design (in most cases), but the computations are different:

    • declexpr(foo(args)) does name lookup for foo, does overload resolution, finds the declaration, returns the exact return type declared, end of story

    • EXPR_DEC_TYPE(foo(args)) finds the type of the declaration then computes

      • the type T of the expression which is the return type naked (without
        reference)

      • the lxrvalueness LXR of the expression according to the referenceness of the declared return type: lvalue for reference, xvalue of r-reference...

      then it decorates the type T with LXR to obtain the decT type:

      • decT is T if LXR = prvalue
      • decT is T&& if LXR = xvalue
      • decT is T& if LXR = lvalue

      EXPR_DEC_TYPE returns decT which is the same as the declared return type.

提交回复
热议问题