从用 decltype(auto) 声明的函数返回一个数组?
Returning an array from a function declared with decltype(auto)?
我想从函数中 return 一个数组(或对数组的引用),如下所示:
decltype(auto) bar() { static int a[2]; return a; }
不幸的是,它会导致非常神秘的错误。 GCC 抱怨:
error: cannot convert 'int [2]' to 'int [2]' in return
而且 Clang 并没有更好地解释问题:
error: array initializer must be an initializer list
演示:https://gcc.godbolt.org/z/ao7Txa9oP
甚至可以从函数 return 数组吗? (我可以摆脱 decltype(auto)
。)
如果不是,那么为什么,前提是函数可以以各种方式接受数组,例如:
void f(auto [2]);
void f(auto (&)[2]);
给定
decltype(auto) bar() { static int a[2]; return a; }
return类型会被decltype
推导为a
的类型,即数组类型为int[2]
,不能指定为return 函数类型。
If the argument is an unparenthesized id-expression or an unparenthesized class member access expression, then decltype yields the type of the entity named by this expression.
如果你想return一个指针作为int*
,你可以
auto bar() { static int a[2]; return a; }
如果你想return引用数组作为int(&)[2]
,你可以
auto& bar() { static int a[2]; return a; }
或者
decltype(auto) bar() { static int a[2]; return (a); }
// ^ ^
// for lvalue expression decltype yields T&; i.e. int(&)[2]
Note that if the name of an object is parenthesized, it is treated as an ordinary lvalue expression, thus decltype(x)
and decltype((x))
are often different types.
确实是decltype(auto)
的问题。 return 类型是通过以下列表推导出来的(所讨论的表达式是给 return 语句的表达式):
[dcl.type.decltype] (redacted for emphasis)
decltype-specifier:
decltype ( expression )
1 For an expression E, the type denoted by decltype(E) is defined
as follows:
- ...
- ...
- otherwise, if E is an unparenthesized id-expression or an unparenthesized class member access ([expr.ref]), decltype(E) is the
type of the entity named by E. If there is no such entity, or if E
names a set of overloaded functions, the program is ill-formed;
- ...
- ...
a
的类型是int[2]
。所以你本质上是用数组 return 类型定义一个函数......但是......
[dcl.fct]
11 Functions shall not have a return type of type array or
function, although they may have a return type of type pointer or
reference to such things. There shall be no arrays of functions,
although there can be arrays of pointers to functions.
您无意中生成了格式错误的函数类型。所以是的,正如您所说,解决方案是不使用 decltype
。您可以显式 return 引用(对推导的数组类型),因为这确实是您想要的:
auto& bar() { static int a[2]; return a; }
或者如果您想明确 return 类型,而不进入声明符地狱,您可以以尾随方式指定它:
auto bar() -> int(&)[2] { static int a[2]; return a; }
无论哪种方式,我都会说它比依赖 decltype
的(有时是神秘的)语义要好。当您显式 return 一个左值引用(当这是您的全部意图)时,没有细微的错误。
顺便说一句,您的最后一个问题包含尚未指定为合法的声明尚未(尽管已被实现接受,因为人们几乎同意它们应该是)。这是CWG Issue 2397。但它的要点是它的行为就像人们期望函数参数中的数组类型一样。 auto [2]
调整为auto*
,而auto(&) [2]
只绑定特定类型的数组。这是一个缩写函数模板,相当于:
template<typename T>
void f(T[2]);
template<typename T>
void f(T(&)[2]);
Return 类型根本没有像参数那样调整。其中参数类型调整本身是 C 的遗产,我们从中得到了不是 first-class 公民的原始数组。随着类型的发展,它们非常不规则。如果您需要数组的“更明智”的值语义,您可以使用 std::array
来达到这个目的。
我想从函数中 return 一个数组(或对数组的引用),如下所示:
decltype(auto) bar() { static int a[2]; return a; }
不幸的是,它会导致非常神秘的错误。 GCC 抱怨:
error: cannot convert 'int [2]' to 'int [2]' in return
而且 Clang 并没有更好地解释问题:
error: array initializer must be an initializer list
演示:https://gcc.godbolt.org/z/ao7Txa9oP
甚至可以从函数 return 数组吗? (我可以摆脱 decltype(auto)
。)
如果不是,那么为什么,前提是函数可以以各种方式接受数组,例如:
void f(auto [2]);
void f(auto (&)[2]);
给定
decltype(auto) bar() { static int a[2]; return a; }
return类型会被decltype
推导为a
的类型,即数组类型为int[2]
,不能指定为return 函数类型。
If the argument is an unparenthesized id-expression or an unparenthesized class member access expression, then decltype yields the type of the entity named by this expression.
如果你想return一个指针作为int*
,你可以
auto bar() { static int a[2]; return a; }
如果你想return引用数组作为int(&)[2]
,你可以
auto& bar() { static int a[2]; return a; }
或者
decltype(auto) bar() { static int a[2]; return (a); }
// ^ ^
// for lvalue expression decltype yields T&; i.e. int(&)[2]
Note that if the name of an object is parenthesized, it is treated as an ordinary lvalue expression, thus
decltype(x)
anddecltype((x))
are often different types.
确实是decltype(auto)
的问题。 return 类型是通过以下列表推导出来的(所讨论的表达式是给 return 语句的表达式):
[dcl.type.decltype] (redacted for emphasis)
decltype-specifier: decltype ( expression )
1 For an expression E, the type denoted by decltype(E) is defined as follows:
- ...
- ...
- otherwise, if E is an unparenthesized id-expression or an unparenthesized class member access ([expr.ref]), decltype(E) is the type of the entity named by E. If there is no such entity, or if E names a set of overloaded functions, the program is ill-formed;
- ...
- ...
a
的类型是int[2]
。所以你本质上是用数组 return 类型定义一个函数......但是......
[dcl.fct]
11 Functions shall not have a return type of type array or function, although they may have a return type of type pointer or reference to such things. There shall be no arrays of functions, although there can be arrays of pointers to functions.
您无意中生成了格式错误的函数类型。所以是的,正如您所说,解决方案是不使用 decltype
。您可以显式 return 引用(对推导的数组类型),因为这确实是您想要的:
auto& bar() { static int a[2]; return a; }
或者如果您想明确 return 类型,而不进入声明符地狱,您可以以尾随方式指定它:
auto bar() -> int(&)[2] { static int a[2]; return a; }
无论哪种方式,我都会说它比依赖 decltype
的(有时是神秘的)语义要好。当您显式 return 一个左值引用(当这是您的全部意图)时,没有细微的错误。
顺便说一句,您的最后一个问题包含尚未指定为合法的声明尚未(尽管已被实现接受,因为人们几乎同意它们应该是)。这是CWG Issue 2397。但它的要点是它的行为就像人们期望函数参数中的数组类型一样。 auto [2]
调整为auto*
,而auto(&) [2]
只绑定特定类型的数组。这是一个缩写函数模板,相当于:
template<typename T>
void f(T[2]);
template<typename T>
void f(T(&)[2]);
Return 类型根本没有像参数那样调整。其中参数类型调整本身是 C 的遗产,我们从中得到了不是 first-class 公民的原始数组。随着类型的发展,它们非常不规则。如果您需要数组的“更明智”的值语义,您可以使用 std::array
来达到这个目的。