Cpp Notes

placeholder_type

About placeholder type: e.g. auto or decltype(auto)

Placeholder Type Specifiers in C++

1. Usage in Variable Declarations:

  • Used in the type specifier sequence of a variable declaration (auto x = expr).
  • Type is deduced from the initializer (expr).
  • auto or decltype(auto) deduces the variable type from the initializer.
  • If the placeholder type specifier is auto or type-constraint auto(since C++20), the variable type is deduced from the initializer using the rules for template argument deduction from a function call For example, given const auto& i = expr;, the type of i is exactly the type of the argument u in an imaginary template, if the function call f(expr) was compiled.
template<class U> void f(const U& u)

2. Multiple Variable Declarations:

  • If used to declare multiple variables, deduced types must match.
  • Example: auto i = 0, d = 0.0 is ill-formed.

3. Usage in New Expressions:

  • Appears in the type-id of a new expression.
  • Type is deduced from the initializer (T is deduced as if for T x init).

4. Return Type of Functions/Lambdas:

  • Can be used in the return type of a function or lambda expression.
  • Return type is deduced from the operand of its non-discarded return statement.

5. Parameter Declaration of Non-type Template Parameters:

  • Appears in the parameter declaration of a non-type template parameter.
  • Type is deduced from the corresponding argument.

decltype(auto) is often considered as "perfect (forwarded) return type"

  • decltype(auto) provides a convenient way to deduce return types from expressions, preserving reference semantics and supporting generic programming scenarios.

C++ Weekly - Ep 344 - decltype(auto): An Overview of How, Why and Where

#include <type_traits>

int global_i = 42;

const int& fnc_return_const_ref() { return global_i; };

decltype(auto) showcase_perfect_return() {
    return fnc_return_const_ref();
}

auto showcase_NOT_perfect_return() {
    // as return is auto here, it does a template parameter
    // deduction on the expression, then as the return type
    return fnc_return_const_ref();
}

int main() {
    // auto is like template parameter deduction for compiler
    auto auto_i = fnc_return_const_ref();
    static_assert(std::is_same_v<decltype(auto_i), int>);

    auto& auto_ref_i = fnc_return_const_ref();
    static_assert(std::is_same_v<decltype(auto_ref_i), const int&>);

    const auto const_auto_i = fnc_return_const_ref();
    static_assert(std::is_same_v<decltype(const_auto_i), const int>);

    const auto& const_auto_ref_i = fnc_return_const_ref();
    static_assert(std::is_same_v<decltype(const_auto_ref_i), const int&>);

    // decltype(auto) deduces the exact type of an expression!
    decltype(auto) decltype_auto_i = fnc_return_const_ref();
    static_assert(std::is_same_v<decltype(decltype_auto_i), const int&>);

    decltype(auto) perfect_return_show_i = showcase_perfect_return();
    static_assert(std::is_same_v<decltype(perfect_return_show_i), const int&>);

    decltype(auto) not_perfect_return_show_i = showcase_NOT_perfect_return();
    static_assert(std::is_same_v<decltype(not_perfect_return_show_i), int>);
}