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

后端 未结 3 1177
慢半拍i
慢半拍i 2021-02-13 03:48

What is the difference between decltype(auto) and decltype(returning expression) as return type of a function (template) if expr used with

相关标签:
3条回答
  • 2021-02-13 04:36

    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.

    0 讨论(0)
  • 2021-02-13 04:36

    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
    0 讨论(0)
  • 2021-02-13 04:38

    Yet another example. In the decltype(auto) case, lambdas are allowed. In the decltype(expr) case, lambdas are disallowed.

    That's because a) lambdas are not allowed as unevaluated operands (such as in sizeof or decltype) and b) the type in the decltype and in the body of the two expressions would be different, since they are two distinct lambda expressions

    // ill-formed: lambda within decltype
    auto f() -> decltype([]{}) { return []{}; };
    
    // allowed: lambda is not in decltype, and only appears once
    auto f() -> decltype(auto) { return []{}; }
    
    0 讨论(0)
提交回复
热议问题