关于其他答案:我们在这里明确处理n3638,以及它是如何合并到最近的 C++1y 草案中的。
我正在使用来自提交人的 github 存储库的9514cc28 ,它已经包含了对 n3638 的一些(次要)修复/更改。
n3638 明确允许:
struct A {
auto f(); // forward declaration
};
auto A::f() { return 42; }
而且,正如我们可以从 [dcl.spec.auto] 中推断的那样,在指定此功能的地方,即使以下内容也是合法的:
struct A {
auto f(); // forward declaration
};
A x;
auto A::f() { return 42; }
int main() { x.f(); }
(但稍后会详细介绍)
这与任何尾随返回类型或从属名称查找根本不同,auto f();
初步声明也是如此,类似于struct A;
. 它需要稍后在使用之前完成(在需要返回类型之前)。
此外,OP 中的问题与内部编译器错误有关。最近的 clang++3.4 主干 192325 Debug+Asserts build 编译失败,因为解析行时断言失败return L.b_ == R.b_;
。到目前为止,我还没有检查过最新版本的 g++。
OP 的示例对 n3638 是否合法?
这对 IMO 来说有点棘手。(在本节中,我总是指 9514cc28。)
1. 什么地方可以使用`auto`?
[dcl.spec.auto]
6 使用auto
或decltype(auto)
在本节未明确允许的上下文中使用的程序是格式错误的。
2 占位符类型可以与函数声明符一起出现在decl-specifier-seq、type-specifier-seq、conversion-function-id或trailing-return-type中,在这样的声明符有效的任何上下文中。
/5 还定义了一些上下文,但它们在这里无关紧要。
因此,auto func()
andauto operator@(..)
通常是允许的(这来自于函数声明的组合 as T D
,其中T
是decl-specifier-seq的形式,并且auto
是一个类型说明符)。
2. 是否允许写`auto func();`,即非定义的声明?
[dcl.spec.auto]/1 说
auto
和decltype(auto)
类型说明符指定稍后将被替换的占位符类型,或者通过从初始化程序中推导,或者通过带有尾随返回类型的显式说明。
和 /2
如果函数声明的返回类型包含占位符类型,则函数的返回类型是从函数return
体中的语句推导出来的,如果有的话。
尽管它没有明确允许像auto f();
函数这样的声明(即没有定义的声明),但从 n3638 和 [dcl.spec.auto]/11 可以清楚地看出它是允许的,而不是明确禁止的.
3.朋友功能呢?
到目前为止,示例
struct A
{
int a_;
friend auto operator==(A const& L, A const& R);
}
auto operator==(A const& L, A const& R)
{ return L.a_ == R.a_; }
应该是格式良好的。现在有趣的部分是定义中的友元函数的定义A
,即
struct A
{
int a_;
friend auto operator==(A const& L, A const& R)
{ return L.a_ == R.a_; } // allowed?
}
在我看来,这是允许的。为了支持这一点,我将引用名称查找。在友元函数声明中定义的函数定义中的名称查找遵循 [basic.lookup.unqual]/9 中的成员函数的名称查找。同一节的 /8 指定了对成员函数体内使用的名称的非限定查找。可以声明使用名称的一种方式是它“应是类的成员或(10.2)X
的基类的成员”。X
这使得广为人知的
struct X
{
void foo() { m = 42; }
int m;
};
请注意如何m
在使用之前未声明 in foo
,但它是X
.
由此,我得出结论,即使
struct X
{
auto foo() { return m; }
int m;
}
被允许。这由 clang++3.4 中继 192325 支持。名称查找需要在struct
完成后解释此函数,还要考虑:
struct X
{
auto foo() { return X(); }
X() = delete;
};
类似地,类中定义的友元函数体只能在类完成后才能解释。
4. 模板呢?
具体来说,怎么friend auto some_function(B const& L) { return L.b_; }
办?
首先,注入的类名 B
等价于B<T>
,请参见 [temp.local]/1。它指的是当前实例化([temp.dep.type]/1)。
id 表达式 L.b_
引用当前实例化的成员( /4)。它也是当前实例化的依赖成员——这是在 C++11 之后添加的,请参阅DR1471,我不知道该怎么想:[temp.dep.expr]/5 states this id -expression不依赖于类型,据我所知 [temp.dep.constexpr] 并没有说它依赖于值。
如果 inL.b_
中的名称不依赖,则名称查找将遵循每个 [temp.nondep] 的“常用名称查找”规则。否则,这会很有趣(依赖名称查找没有很好地指定),但考虑到
template<class T>
struct A
{
int foo() { return m; }
int m;
};
大多数编译器也接受了,我认为带有的版本auto
也应该是有效的。
[temp.friend] 中还有一个关于模板之友的部分,但 IMO 并没有说明此处的名称查找。
还可以在 isocpp-forum 中查看这个高度相关的讨论。