为什么在使用decltype时,编译器为什么不推断成员的类型?

时间:2018-12-12 13:15:15

标签: c++ c++11 language-lawyer decltype

我只是在自己的代码中注意到了这种行为,所以这是一个天真的问题:

此:

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上进行了测试

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 ...;
    }
};

我希望其他答案能告诉您该语言的确切规则,这些规则会阻止编写此类内容。我试图给出一种动机,为什么我们不能写它实际上更好。