【问题标题】:Why doesn't the compiler infer the type of member when using decltype? [duplicate]为什么编译器在使用 decltype 时不推断成员的类型? [复制]
【发布时间】:2019-05-13 14:36:46
【问题描述】:

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

这个:

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 测试

【问题讨论】:

  • 编译器在知道它存在之前就无法推断出它的类型
  • 两个不同的东西。声明和定义。在类声明完成之前,不会定义类方法。在声明类方法和成员(它们的类型和返回类型)时,通常的规则适用。无法引用尚未声明的内容。
  • 但是在第一个 sn-p 中,它确实知道 a,不是吗?
  • 它“知道 a”,因为在声明整个类之前,逻辑上没有定义函数的主体。就好像编译器在看到右大括号之前不会处理类方法的主体。
  • 请注意类似的事情确实可以编译:struct A { size_t get() { return sizeof(a); } int a; };

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


【解决方案1】:

在您的第一个示例中,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;
};

【讨论】:

  • @Bathsheba 为什么不呢?声明 size_t get() 不包含任何关于 a 的内容。
  • 你是对的。自从我拖了你的答案以来,请投赞成票。我也非正式地回答过。
  • 我并不是真正会问关于正确的正式术语的问题,但我想知道“(因为 A 是完全定义的)”是否真的正确。其成员的定义不是As 定义的一部分吗?您的意思是写“因为 A 已完全声明”?
  • @user463035818 是的,虽然我认为我从未遇到过“类定义”的正式定义。我不知道标准是否定义了这一点,或者区分类声明和类定义。但我想你可以说类定义包括其成员的定义。
  • 好吧,抱歉,再想一想,我的“更正”让情况变得更糟了。 struct A; 已经是声明(但没有定义),不确定“完全声明”的正确措辞是什么
【解决方案2】:

如果可以的话,一个非正式的回答:decltype 是一个红鲱鱼。

return 类型的所有部分都需要在第一次传递时已经被编译器看到。

例如,

template <size_t N> struct T{};

struct A
{
    T<sizeof(a)> get()
    {
        return T<sizeof(a)>();
    }
    int a;
};

也会失败,原因相同:如果在get() 之前看到int a;,则编译通过。

【讨论】:

【解决方案3】:

让我来解释一下为什么你想写的东西不能正常工作:

struct A {
    auto get() -> decltype(a)
    {
        return a;
    } 
    int a=1;
};

如果允许这样做,则要么需要超级复杂的规则,要么还允许以下规则,但实际上没有意义:

struct cyclic {
    auto a() -> decltype(b) {
        return ...;
    }
    auto b() -> decltype(a) { 
        return ...;
    }
};

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

【讨论】:

  • 嗯,我明白了,但不确定它是否适用于这里:定义 a() 时,查找 b 的类型确实会失败。相反,在我的示例中,数据成员的类型是明确已知的。
  • @kebs 这就是“超级复杂的规则”是必需的。当前的简单规则是“成员按照它们在代码中出现的顺序声明”。将此与“成员以任何顺序声明,但如果一个成员的声明依赖于第二个成员的声明进行比较,则它们需要按顺序声明 [... ~500 个特殊情况、注释和怪癖,因为干扰其他规则.. . ]" ;)
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-03-11
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多