Derivation of C++ decltype type

decltype specifier (C++11)

C++11 introduces a decltype specifier, which can obtain the type of an object or expression.

It is also easy to use:

#include <iostream>

int main() {
    int x1 = 0;
    decltype(x1) x2 = 0;        // x2 is derived as int type (type of variable x1)
    std::cout << typeid(x2).name() << std::endl; 

    double x3 = 0;
    decltype(x1 + x3) x4 = x1 + x3;     // x4 is derived as double type (type of expression x1 + x3)
    std::cout << typeid(x4).name() << std::endl;
    return 0;
}

Operation results:

linrongjian@hhb-ssd:/data/modern_cpp$ g++ -std=c++11 decltype_example.cpp -o decltype_example
linrongjian@hhb-ssd:/data/modern_cpp$ ./decltype_example 
i
d

Compared with auto, decltype can be used to deduce the type of non static member variables in a class in addition to the expression type (auto is not allowed),

Use decltype to retain the reference attribute of auto (C++14)

When I talked about the auto keyword earlier, I said that one of the derivation rules of auto is that when using auto to declare variables for initialization, if the target object is a reference, the reference attribute will be ignored.

For example, the following code:

#include <iostream>
#include <cassert>
#include <type_traits>

template <class T>
auto return_ref(T& t) {
    return t;
}

int main() {
    int x1 = 0;
    static_assert(std::is_reference_v<decltype(return_ref(x1))>);
}

We refer to the passed parameter t, hoping to return_ref can return a reference, but in fact auto is derived as a value type here:

One solution is to add &, and the other is to use decltype as the trailing return type:

#include <iostream>
#include <cassert>
#include <type_traits>

template <class T>
auto return_ref(T& t) -> decltype(t) {
    return t;
}

int main() {
    int x1 = 0;
    static_assert(std::is_reference_v<decltype(return_ref(x1))>);
}

Derivation rules of decltype

For a variable or expression e of type T, there are five derivation rules for decltype(e):

  1. If e is an unsupported expression (except for structured binding) or an unsupported class member access, decltype(e) infers that the type of E is T.

    If there is no such type (for example, the variable in the expression is not in the current scope) or e is an overloaded function (runtime polymorphism, decltype requires that the return type be determined at compile time), derivation cannot be performed.

  2. If e is a function call or a function like call, the type inferred by decltype(e) is the type of its return value.

  3. If e is an lvalue of type T, decltype(e) is T &.

    This is why the above example returns a reference.

  4. If e is a dying value of type T, decltype(e) is T & &.

  5. In other cases, decltype(e) is T.

Derivation of cv qualifier (const / volatile)

decltype(e) the derived type synchronizes the cv qualifier of e.

For example:

const int i = 0;
decltype(i);	// declytpe(i) derivation type is const (note the difference between auto and declytpe)

If e is a member variable of a class, you need to add parentheses to synchronize the cv qualifier.

struct A {
    double x;
};
const A* a = new A();
decltype(a->x);		// Decltype (a - > x) derivation type is double, const attribute is ignored

The above code does not add parentheses to a - > x, and the derived type does not have const attribute. After parentheses, the derived type is const double:

struct A {
    double x;
};
const A* a = new A();
decltype((a->x));	// Decltype ((a - > x)) derivation type is const double

Conclusion: when e is a data member with parentheses, the cv qualifier of the parent object expression will be synchronized to the derivation result.

decltype(auto) (C++14)

The C++14 runtime uses decltype(auto) to tell the compiler to use the rules of decltype to deduce auto

decltype(auto) must be used alone, that is, it can't be added with pointers, references and cv attributes.

Look at the previous return_ref example, if we still want to use Auto to return_ The ref function returns a reference, but we do not want to use auto & (in the case of generic programming, it is not enough to only express the expression of reference semantics), so we can use decltype(auto):

#include <iostream>
#include <cassert>
#include <type_traits>

template <class T>
decltype(auto) return_ref(T& t) {
    return t;
}

int main() {
    int x1 = 0;
    static_assert(std::is_reference_v<decltype(return_ref(x1))>);
}

Here, because decltype(auto) is used, auto derives the type according to the rules of decltype. Here, e is an lvalue reference. According to the derivation rule 3, a reference is returned, so our assertion can pass.

reference material

  1. Analysis of the core features of modern C + + language
  2. cppreference: decltype specifier

Tags: C++ Algorithm programming language

Posted by bimbo on Wed, 07 Sep 2022 22:08:45 +0300