关于其他回答:我们在这里明确地处理的是
n3638以及它如何被纳入最近的C++1y草案中。
我正在使用来自
委员会的Github存储库的9514cc28,它已经包含了一些(轻微的)对n3638的修复/更改。
n3638允许明确地:
struct A {
auto f();
};
auto A::f() { return 42; }
而且,正如我们可以从[dcl.spec.auto]推断出的那样,在此功能被指定的地方,甚至以下内容也是合法的:
struct A {
auto f();
};
A x;
auto A::f() { return 42; }
int main() { x.f(); }
(但稍后会更详细地讨论)这与任何尾随返回类型或相关名称查找 fundamentally 不同,因为
auto f();
是一个初步声明,类似于
struct A;
。它需要在使用之前(在需要返回类型之前)完成。
此外,OP中的问题与内部编译器错误有关。最近的clang++3.4 trunk 192325 Debug + Asserts版本无法编译,因为在解析行return L.b_ == R.b_;
时出现了断言失败。截至目前,我还没有检查过g ++的最新版本。
OP的例子在n3638方面是否合法?
我认为这有点棘手。(在这一节中,我总是指9514cc28。)
1.允许在哪里使用“auto”?
[dcl.spec.auto]
“使用
auto
或
decltype(auto)
在本节中未明确允许的情况下,程序是不合法的。占位符类型可以与函数声明符一起出现在
decl-specifier-seq、
type-specifier-seq、
conversion-function-id或
trailing-return-type中,在任何这样的声明符有效的上下文中。因此,
auto func()
和
auto operator@(..)
通常是允许的(这是由函数声明的组成形式
T D
决定的,其中
T
是
decl-specifier-seq的形式,而
auto
是一个
type-specifier)。”
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_; }
}
在我看来,这是允许的。为了支持这一点,我会引用名称查找。在友元函数声明中定义的函数的定义内部进行名称查找遵循成员函数的名称查找,如[basic.lookup.unqual]/9所述。在同一节的/8中,指定了名称在成员函数体内使用的无资格名称查找。一个名称被声明为被使用的方式之一是它“必须是类X的成员或者是X的一个基类的成员(10.2)”。这允许使用广泛知道的方法。
struct X
{
void foo() { m = 42; }
int m;
};
注意在
foo
中使用
m
之前没有声明它,但它是
X
的一个成员。
由此可以得出结论,即即使
struct X
{
auto foo() { return m; }
int m;
}
允许这样做。这得到了clang++3.4 trunk 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-expression” L.b_
指代“当前实例化”的一个成员(/4)。它也是“当前实例化”的一个“相关成员”,这是 C++11 之后增加的内容,参见 DR1471,我不知道该怎么看待它:[temp.dep.expr]/5 表示这个“id-expression”不依赖于类型,就我所知 [temp.dep.constexpr] 也没有表明它依赖于值。
如果
L.b_
中的名称不是依赖项,则名称查找将遵循[ temp.nondep ]的“通常名称查找”规则。否则,这将很有趣(依赖项名称查找未经过很好的指定),但考虑到…
template<class T>
struct A
{
int foo() { return m; }
int m;
};
大多数编译器也接受带有 "auto" 的版本,我认为这个版本也应该是有效的。在 [temp.friend] 中还有一个关于模板友元的部分,但我认为它对此处的名称查找没有什么帮助。
另请参见
isocpp-forum中这个高度相关的讨论。
main
函数中第二个比较被注释掉,clang++ 3.4 (trunk 184460)可以编译这个例子。这可能追溯到类模板中定义的友元函数只有在odr-used时才会实例化(就我所看到的而言,标准中没有定义实例化的时机)。 - dyp