decltype(auto) 和 decltype(returning expr) 作为 return 类型有什么区别?
What is difference between decltype(auto) and decltype(returning expr) as return type?
如果 decltype(auto)
和 decltype(returning expression)
作为 return 类型的函数(模板)有什么区别,如果 expr
在两种情况下都没有括号使用?
auto f() -> decltype(auto) { return expr; } // 1
auto f() -> decltype(expr) { return expr; } // 2
以上 f
在任何上下文中都可以是 defined/declared,并且可以是(成员)函数或(成员)函数模板,甚至是(通用)lambda。 expr
可以依赖于任何模板参数。
在第二个版本中,两个 expr
是完全相同的表达式,没有额外的括号。
在 C++14 及更高版本中使用第一种或第二种形式时,可以预料到哪些差异?
如果到处都使用括号会怎样?
是的,有区别。第一个将根据函数体中的 return 表达式检测 return 类型。
第二个不会把return类型也设置成decltype()
里面表达式的类型,还会在上面套用expression 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{});
这是 select 正确的重载。现在,如果我们用 decltype(auto)
替换 decltype(expr)
,编译器将无法 select 正确的重载,因为函数签名 中没有 限制类型应该能够做什么。
decltype(auto)
用于
转发return输入通用代码,你不想输入很多重复的东西
template<class Func, class... Args>
decltype(auto) foo(Func f, Args&&... args)
{
return f(std::forward<Args>(args)...);
}
延迟类型推导,如您在 this question 中所见,编译器在输出 decltype(auto)
时存在一些问题
- 中看到的那样效果不佳
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)
:
- 应用 SFINAE 而
decltype(auto)
不应用
又一个例子。在 decltype(auto)
情况下,允许使用 lambda。在 decltype(expr)
情况下,不允许使用 lambda。
那是因为 a) lambda 不允许作为未计算的操作数(例如在 sizeof
或 decltype
中)和 b) decltype
和正文中的类型两个表达式会有所不同,因为它们是两个不同的 lambda 表达式
// ill-formed: lambda within decltype
auto f() -> decltype([]{}) { return []{}; };
// allowed: lambda is not in decltype, and only appears once
auto f() -> decltype(auto) { return []{}; }
如果 decltype(auto)
和 decltype(returning expression)
作为 return 类型的函数(模板)有什么区别,如果 expr
在两种情况下都没有括号使用?
auto f() -> decltype(auto) { return expr; } // 1
auto f() -> decltype(expr) { return expr; } // 2
以上 f
在任何上下文中都可以是 defined/declared,并且可以是(成员)函数或(成员)函数模板,甚至是(通用)lambda。 expr
可以依赖于任何模板参数。
在第二个版本中,两个 expr
是完全相同的表达式,没有额外的括号。
在 C++14 及更高版本中使用第一种或第二种形式时,可以预料到哪些差异?
如果到处都使用括号会怎样?
是的,有区别。第一个将根据函数体中的 return 表达式检测 return 类型。
第二个不会把return类型也设置成decltype()
里面表达式的类型,还会在上面套用expression 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{});
这是 select 正确的重载。现在,如果我们用 decltype(auto)
替换 decltype(expr)
,编译器将无法 select 正确的重载,因为函数签名 中没有 限制类型应该能够做什么。
decltype(auto)
用于
转发return输入通用代码,你不想输入很多重复的东西
template<class Func, class... Args> decltype(auto) foo(Func f, Args&&... args) { return f(std::forward<Args>(args)...); }
延迟类型推导,如您在 this question 中所见,编译器在输出
时存在一些问题decltype(auto)
- 中看到的那样效果不佳
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)
:
- 应用 SFINAE 而
decltype(auto)
不应用
又一个例子。在 decltype(auto)
情况下,允许使用 lambda。在 decltype(expr)
情况下,不允许使用 lambda。
那是因为 a) lambda 不允许作为未计算的操作数(例如在 sizeof
或 decltype
中)和 b) decltype
和正文中的类型两个表达式会有所不同,因为它们是两个不同的 lambda 表达式
// ill-formed: lambda within decltype
auto f() -> decltype([]{}) { return []{}; };
// allowed: lambda is not in decltype, and only appears once
auto f() -> decltype(auto) { return []{}; }