Are there any realistic use cases for `decltype(auto)` variables?

前端 未结 2 877
借酒劲吻你
借酒劲吻你 2021-01-31 09:44

Both from my personal experience and from consulting answers to questions like What are some uses of decltype(auto)? I can find plenty of valuable use cases for decltype(a

相关标签:
2条回答
  • 2021-01-31 09:50

    Probably not a very deep answer, but basically decltype(auto) was proposed to be used for return type deduction, to be able to deduce references when the return type is actually a reference (contrary to plain auto that will never deduce the reference, or auto&& that will always do it).

    The fact that it can also be used for variable declaration not necessarily means that there should be better-than-other scenarios. Indeed, using decltype(auto) in variable declaration will just complicate the code reading, given that, for a variable declaration, is has exactly the same meaning. On the other hand, the auto&& form allows you to declare a constant variable, while decltype(auto) doesn't.

    0 讨论(0)
  • 2021-01-31 10:06

    Essentially, the case for variables is the same for functions. The idea is that we store the result of an function invocation with a decltype(auto) variable:

    decltype(auto) result = /* function invocation */;
    

    Then, result is

    • a non-reference type if the result is a prvalue,

    • a (possibly cv-qualified) lvalue reference type if the result is a lvalue, or

    • an rvalue reference type if the result is an xvalue.

    Now we need a new version of forward to differentiate between the prvalue case and the xvalue case: (the name forward is avoided to prevent ADL problems)

    template <typename T>
    T my_forward(std::remove_reference_t<T>& arg)
    {
        return std::forward<T>(arg);
    }
    

    And then use

    my_forward<decltype(result)>(result)
    

    Unlike std::forward, this function is used to forward decltype(auto) variables. Therefore, it does not unconditionally return a reference type, and it is supposed to be called with decltype(variable), which can be T, T&, or T&&, so that it can differentiate between lvalues, xvalues, and prvalues. Thus, if result is

    • a non-reference type, then the second overload is called with a non-reference T, and a non-reference type is returned, resulting in a prvalue;

    • an lvalue reference type, then the first overload is called with a T&, and T& is returned, resulting in an lvalue;

    • an rvalue reference type, then the second overload is called with a T&&, and T&& is returned, resulting in an xvalue.

    Here's an example. Consider that you want to wrap std::invoke and print something to the log: (the example is for illustration only)

    template <typename F, typename... Args>
    decltype(auto) my_invoke(F&& f, Args&&... args)
    {
        decltype(auto) result = std::invoke(std::forward<F>(f), std::forward<Args>(args)...);
        my_log("invoke", result); // for illustration only
        return my_forward<decltype(result)>(result);
    }
    

    Now, if the invocation expression is

    • a prvalue, then result is a non-reference type, and the function returns a non-reference type;

    • a non-const lvalue, then result is a non-const lvalue reference, and the function returns a non-const lvalue reference type;

    • a const lvalue, then result is a const lvalue reference, and the function returns a const lvalue reference type;

    • an xvalue, then result is an rvalue reference type, and the function returns an rvalue reference type.

    Given the following functions:

    int f();
    int& g();
    const int& h();
    int&& i();
    

    the following assertions hold:

    static_assert(std::is_same_v<decltype(my_invoke(f)), int>);
    static_assert(std::is_same_v<decltype(my_invoke(g)), int&>);
    static_assert(std::is_same_v<decltype(my_invoke(h)), const int&>);
    static_assert(std::is_same_v<decltype(my_invoke(i)), int&&>);
    

    (live demo, move only test case)

    If auto&& is used instead, the code will have some trouble differentiating between prvalues and xvalues.

    0 讨论(0)
提交回复
热议问题