用decltype(auto)声明的函数返回数组?

4

我想要从一个函数中返回一个数组(或数组的引用),方法如下:

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

从函数中返回数组是可能的吗?(如果可以丢掉decltype(auto),我也没关系)

如果不行,为什么呢?因为函数可以以各种方式接受数组,例如:

void f(auto [2]);
void f(auto (&)[2]);
2个回答

4
问题确实出在 decltype(auto) 上。返回类型是通过遵循以下列表进行推断的(所讨论的表达式是给定给返回语句的表达式):

[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;
  • ...
  • ...
< p > a的类型是int [2]。因此,您实际上正在定义一个带有数组返回类型的函数...但是...

[dcl.fct]

11函数不得具有数组或函数类型的返回类型,尽管它们可以具有指向这些东西的指针或引用类型的返回类型。不能有函数的数组,尽管可以有指向函数的指针数组。

您无意中生成了一个格式错误的函数类型。因此,是的,解决方案就像您注意到的那样,不要使用decltype。您可以显式返回引用(指向推断出的数组类型),因为这确实是您想要的:

auto& bar() { static int a[2]; return a; }

或者如果您想明确返回类型,而不必进入声明符地狱,您可以在尾随方式中指定它:
auto bar() -> int(&)[2] { static int a[2]; return a; }

无论如何,我认为这比依赖于有时晦涩难懂的decltype语义要好。当你明确返回一个左值引用(这是你的全部意图)时,就不会出现微妙的错误。


作为旁注,您最后一个问题包含了尚未被规定为合法的声明(虽然由于它们基本上被认为是正确的实现而被接受)。这是CWG Issue 2397。但其要点是,它的行为就像在函数参数中预期的数组类型一样。 auto [2] 被调整为 auto*,而 auto(&) [2] 仅绑定到特定类型的数组。这意味着它是一个缩写的函数模板,相当于:
template<typename T>
void f(T[2]);

template<typename T>
void f(T(&)[2]);

返回类型不像参数那样进行调整。参数类型的调整本身是C遗产,我们从中得到了不是一等公民的原始数组。就类型而言,它们相当不规则。如果您需要具有“更健全”的值语义的数组,则可以使用std::array来实现此目的。

嗯,return (a); - Yakk - Adam Nevraumont
@Yakk-AdamNevraumont - 不,我不会给出在我看来是错误建议,只是为了炫耀我的C++知识。auto &是一个引用,对于初学者来说,其中包含的问题要少得多。 - StoryTeller - Unslander Monica

3

鉴于

decltype(auto) bar() { static int a[2]; return a; }

使用decltype,返回类型将被推断为a的类型,即数组类型int[2],该类型无法指定为函数的返回类型。

如果参数是未加括号的标识符表达式或未加括号的类成员访问表达式,则decltype生成此表达式所命名的实体的类型。

如果您想返回指针类型int*,可以这样做:

auto bar() { static int a[2]; return a; }

如果你想返回一个指向数组的引用,类型为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]

请注意,如果一个对象的名称被括起来,则它将被视为一个普通的左值表达式,因此 decltype(x)decltype((x)) 通常是不同的类型。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接