Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

What is difference between decltype(auto) and decltype(returning expr) as return type?

What is the difference between decltype(auto) and decltype(returning expression) as return type of a function (template) if expr used without parentheses in both cases?

auto f() -> decltype(auto) { return expr; } // 1
auto f() -> decltype(expr) { return expr; } // 2

Above f can be defined/declared in any context and can be either (member) function or (member) function template, or even (generic) lambda. expr can depend on any template parameters.

In second version both expr are exactly the same expression without extra parentheses.

Which differences can one expect, using first or second form in C++14 and later?

What if parentheses used everywhere?

like image 574
Tomilov Anatoliy Avatar asked Nov 04 '16 18:11

Tomilov Anatoliy


People also ask

What is the return type of decltype?

If the expression parameter is a call to a function or an overloaded operator function, decltype(expression) is the return type of the function. Parentheses around an overloaded operator are ignored. If the expression parameter is an rvalue, decltype(expression) is the type of expression .

How does decltype work in C++?

In the C++ programming language, decltype is a keyword used to query the type of an expression. Introduced in C++11, its primary intended use is in generic programming, where it is often difficult, or even impossible, to express types that depend on template parameters.

What does AUTO mean in C++?

The auto keyword is a simple way to declare a variable that has a complicated type. For example, you can use auto to declare a variable where the initialization expression involves templates, pointers to functions, or pointers to members.


2 Answers

Yes there is a difference. The first one will detect the return type based on the return expression in the body of the function.

The second one will not also set the return type to the type of the expression inside decltype(), but will also apply expression sfinae on it. That means that if the expression inside decltype is not valid, the compiler will search for another valid overload. Whereas the first version will be a hard error.

Take this example:

template<typename T>
auto fun(T a) -> decltype(a.f()) { return a.f(); }

template<typename T>
auto fun(T a) -> decltype(a.g()) { return a.g(); }

struct SomeType {
    int g() { return 0; }
};

fun(SomeType{});

This select the correct overload. Now if we replace decltype(expr) by decltype(auto), the compiler will not be able to select the correct overload, as there's nothing in the function signature that constrains what the type is supposed to be able to do.

like image 150
Guillaume Racicot Avatar answered Oct 10 '22 02:10

Guillaume Racicot


decltype(auto) is used to

  • Forward return type in generic code, you don't want to type a lot of duplicate thing

    template<class Func, class... Args>
    decltype(auto) foo(Func f, Args&&... args) 
    { 
        return f(std::forward<Args>(args)...); 
    }
    
  • Delay type deduction, as you can see in this question, compilers have some problem with out decltype(auto)

    • This doesn't work well as you can see with g++ and clang++

      template<int i> struct Int {};
      constexpr auto iter(Int<0>) -> Int<0>;
      
      template<int i> constexpr auto iter(Int<i>) -> decltype(iter(Int<i-1>{}));
      
      int main(){
        decltype(iter(Int<10>{})) a;
      }
      
    • This works well as you can see here:

      template<int i> struct Int {};
      
      constexpr auto iter(Int<0>) -> Int<0>;
      
      template<int i>
      constexpr auto iter(Int<i>) -> decltype(auto) {
        return iter(Int<i-1>{});
      }
      
      int main(){
        decltype(iter(Int<10>{})) a;
      }
      

decltype(expr):

  • applies SFINAE while decltype(auto) is not
like image 26
Danh Avatar answered Oct 10 '22 03:10

Danh