我只是在自己的代码中注意到了这种行为,所以这是一个天真的问题:
此:
struct A
{
int get()
{
return a;
}
int a=1;
};
int main() {}
当然可以编译,尽管当成员数据的声明位于函数定义的之后。
但后来我不明白为什么这样做:
struct A
{
auto get() -> decltype(a)
{
return a;
}
int a=1;
};
不编译(*)。我必须写这个:
struct A
{
int a=1;
auto get() -> decltype(a)
{
return a;
}
};
是否有任何与语言相关的原因,使它不正常?或者仅仅是编译器没有实现?无论类成员的顺序如何,我都希望具有相同的行为。
(*)通过Ideone.com在gcc 6.3上进行了测试
答案 0 :(得分:5)
在您的第一个示例中,先声明A::get
,然后声明A::a
,然后再声明A
(因为已完全声明A::get
)。此时,在A::get
中,编译器知道了A::a
。
考虑以下等效形式:
struct A
{
int get();
int a=1;
};
inline int A::get()
{
return a;
}
这对于您的第二个示例来说是无法编译的:
struct A
{
auto get() -> decltype(a); // What is "a"?
int a=1;
};
inline auto A::get() -> decltype(A::a)
{
return a;
}
声明类型时必须遵循相同的“声明顺序”,例如:
struct A
{
using IntType = int;
auto get() -> IntType;
int a=1;
};
你不能这样写:
struct A
{
using IntType = decltype(a);
auto get() -> IntType;
int a=1;
};
还要注意,这不仅限于返回类型:参数类型也是函数声明的一部分。因此,这也不会编译:
struct A
{
void f(decltype(a));
int a=1;
};
答案 1 :(得分:5)
我可以非正式地回答:decltype
是一个红鲱鱼。
return
类型的所有部分都必须已经在编译器第一次通过时就被看到。
例如,
template <size_t N> struct T{};
struct A
{
T<sizeof(a)> get()
{
return T<sizeof(a)>();
}
int a;
};
也会由于相同的原因而失败:如果在int a;
之前看到get()
,编译就会通过。
答案 2 :(得分:4)
让我给出一些手工解释,为什么您想要编写的内容通常无法正常工作:
struct A {
auto get() -> decltype(a)
{
return a;
}
int a=1;
};
如果允许这样做,那么要么要求使用超级复杂的规则,要么允许以下规则,但实际上没有意义:
struct cyclic {
auto a() -> decltype(b) {
return ...;
}
auto b() -> decltype(a) {
return ...;
}
};
我希望其他答案能告诉您该语言的确切规则,这些规则会阻止编写此类内容。我试图给出一种动机,为什么我们不能写它实际上更好。