decltype(auto)和decltype(返回expr)作为返回类型有什么区别?

时间:2016-11-04 18:01:57

标签: c++ c++14 auto decltype

exprauto f() -> decltype(auto) { return expr; } // 1 auto f() -> decltype(expr) { return expr; } // 2 作为函数(模板)的返回类型有什么区别,如果f在两种情况下都没有使用括号?

expr

以上expr可以在任何上下文中定义/声明,可以是(成员)函数或(成员)函数模板,甚至是(泛型)lambda。 /api/register可以依赖于任何模板参数。

在第二个版本中,/api/register只是没有额外括号的完全相同的表达式。

在C ++ 14及更高版本中使用第一种或第二种形式可以预期哪些差异?

如果在任何地方使用括号怎么办?

2 个答案:

答案 0 :(得分:14)

是的,有区别。第一个将根据函数体中的返回表达式检测返回类型。

第二个也不会将返回类型设置为decltype()内的表达式类型,但也会在其上应用表达式sfinae 。这意味着如果decltype中的表达式无效,编译器将搜索另一个有效的重载。而第一个版本将是一个硬错误。

举个例子:

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{});

选择正确的过载。现在,如果我们将decltype(expr)替换为decltype(auto),编译器将无法选择正确的重载,因为函数签名中没有任何限制类型应该是什么能够做到。

答案 1 :(得分:6)

decltype(auto)用于

  • 在通用代码中转发返回类型,您不想输入大量重复的内容

    template<class Func, class... Args>
    decltype(auto) foo(Func f, Args&&... args) 
    { 
        return f(std::forward<Args>(args)...); 
    }
    
  • 延迟类型扣除,正如您在this question中看到的那样,编译器出现问题decltype(auto)

    • 使用g++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;
      }
      
    • 这很有效,因为您可以看到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)

  • decltype(auto)不是
  • 时应用SFINAE