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

Ori*_*ent 13 c++ decltype auto c++14

如果在两种情况下都没有使用括号,那么函数(模板)的返回类型decltype(auto)decltype(returning expression)返回类型之间有什么区别expr

auto f() -> decltype(auto) { return expr; } // 1
auto f() -> decltype(expr) { return expr; } // 2
Run Code Online (Sandbox Code Playgroud)

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

在第二个版本中,两者expr都是完全相同的表达式,没有额外的括号.

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

如果括号到处使用怎么办?

Gui*_*cot 15

是,有一点不同.第一个将根据函数体中的返回表达式检测返回类型.

第二个也不会将返回类型设置为内部表达式的类型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{});
Run Code Online (Sandbox Code Playgroud)

这选择了正确的过载.现在,如果我们替换decltype(expr)decltype(auto),编译器将无法选择正确的重载,因为函数签名中没有任何内容限制类型应该能够执行的操作.


Dan*_*anh 6

decltype(auto) 习惯了

  • 在通用代码中转发返回类型,你不想输入很多重复的东西

    template<class Func, class... Args>
    decltype(auto) foo(Func f, Args&&... args) 
    { 
        return f(std::forward<Args>(args)...); 
    }
    
    Run Code Online (Sandbox Code Playgroud)
  • 延迟类型扣除,正如你在这个问题中看到的,编译器有一些问题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;
      }
      
      Run Code Online (Sandbox Code Playgroud)
    • 这可以很好地工作,你可以在这里看到:

      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;
      }
      
      Run Code Online (Sandbox Code Playgroud)

decltype(expr):

  • 适用SFINAE而decltype(auto)不是