3

这段代码:

#include <iostream>
struct Acc {
    int a;
};
struct Buu {
    int b;
};

struct Foo {
    const Acc& acc;
    Buu& buu;
};

void printInfo( const Foo& ) {
    std::cout << "hi!" << std::endl;
}

void call( Buu& buu ) {
    Acc acc = { 1 };
    Foo foo = {
        .acc = acc,
        .buu = buu,
    };
    std::cout << "before" << std::endl;
    printInfo( foo );
    std::cout << "after" << std::endl;
}
void noCall( Buu& buu ) {
    Acc acc = { 1 };
    Foo foo = {
        .buu = buu,
        .acc = acc,
    };
    std::cout << "before" << std::endl;
    printInfo( foo );
    std::cout << "after" << std::endl;
}

int main() {
    Buu buu = { 2 };
    call( buu );
    noCall( buu );
    return 0;
}

当由 clang 编译时(我试过 3.7.0、3.7.1)会输出:

before
hi!
after
before
after

printInfo删除了第二个call调用noCall...

使用-pedantic选项它会产生警告,指定初始化器是 C99 的特性,但不是 C++,但仍然创建代码而无需第二次调用printInfo.

它是已知的错误吗?

4

1 回答 1

3

如果不是错误,我认为这至少是不公平的,因为只有当 Clang 简单地删除对fooin function 的所有引用时,警告才处于迂腐级别nocall。我们可以通过在调试模式 ( ) 下查看汇编代码来确认它,c++ -S -g file.cpp以准确了解编译器如何解释每一行。

当我们查看 .s 生成的文件时,我们可以看到在 call 中,生成了第 20Foo foo = {...和 25行printInfo(foo)

    .loc    1 20 0                  # ess.cpp:20:0
    movq    %rcx, -64(%rbp)
    movq    -40(%rbp), %rcx
.Ltmp45:
    movq    %rcx, -56(%rbp)
    .loc    1 24 0                  # ess.cpp:24:0
    movq    %rax, %rdi
    callq   _ZNSt3__1lsINS_11char_traitsIcEEEERNS_13basic_ostreamIcT_EES6_PKc
    leaq    -64(%rbp), %rdi
    leaq    _ZNSt3__14endlIcNS_11char_traitsIcEEEERNS_13basic_ostreamIT_T0_EES7_, %rcx
    movq    %rax, -24(%rbp)
    movq    %rcx, -32(%rbp)
    movq    -24(%rbp), %rax
    .loc    5 322 0                 # /usr/include/c++/v1/ostream:322:0
.Ltmp46:
    movq    %rdi, -72(%rbp)         # 8-byte Spill
    movq    %rax, %rdi
    callq   *-32(%rbp)
.Ltmp47:
    .loc    1 25 0                  # ess.cpp:25:0
    movq    -72(%rbp), %rdi         # 8-byte Reload
    movq    %rax, -80(%rbp)         # 8-byte Spill
    callq   _Z9printInfoRK3Foo
    leaq    _ZNSt3__14coutE, %rdi
    leaq    .L.str2, %rsi

但是对于 nocall,相应的行(30 和 35)不是:

    .loc    1 29 0 prologue_end     # ess.cpp:29:0
.Ltmp57:
    movl    .L_ZZ6noCallR3BuuE3acc, %ecx
    movl    %ecx, -48(%rbp)
    .loc    1 34 0                  # ess.cpp:34:0
    movq    %rax, %rdi
    callq   _ZNSt3__1lsINS_11char_traitsIcEEEERNS_13basic_ostreamIcT_EES6_PKc
    leaq    _ZNSt3__14coutE, %rdi
    leaq    .L.str2, %rsi
    leaq    _ZNSt3__14endlIcNS_11char_traitsIcEEEERNS_13basic_ostreamIT_T0_EES7_, %rdx
    movq    %rax, -24(%rbp)
    movq    %rdx, -32(%rbp)
    movq    -24(%rbp), %rax
    .loc    5 322 0                 # /usr/include/c++/v1/ostream:322:0
.Ltmp58:
    movq    %rdi, -72(%rbp)         # 8-byte Spill
    movq    %rax, %rdi
    movq    %rsi, -80(%rbp)         # 8-byte Spill
    callq   *-32(%rbp)
.Ltmp59:
    .loc    1 36 0                  # ess.cpp:36:0
    movq    -72(%rbp), %rdi         # 8-byte Reload
    movq    -80(%rbp), %rsi         # 8-byte Reload
    movq    %rax, -88(%rbp)         # 8-byte Spill
    callq   _ZNSt3__1lsINS_11char_traitsIcEEEERNS_13basic_ostreamIcT_EES6_PKc
    leaq    _ZNSt3__14endlIcNS_11char_traitsIcEEEERNS_13basic_ostreamIT_T0_EES7_, %rdx
    movq    %rax, -8(%rbp)
    movq    %rdx, -16(%rbp)
    movq    -8(%rbp), %rdi
    .loc    5 322 0                 # /usr/include/c++/v1/ostream:322:0
.Ltmp60:
    callq   *-16(%rbp)
.Ltmp61:
    .loc    1 37 0                  # ess.cpp:37:0

cpp 文件中编号的行是:

18  void call( Buu& buu ) {
19      Acc acc = { 1 };
20      Foo foo = {
21          .acc = acc,
22          .buu = buu,
23      };
24      std::cout << "before" << std::endl;
25      printInfo( foo );
26      std::cout << "after" << std::endl;
27  }
28  void noCall( Buu& buu ) {
29      Acc acc = { 1 };
30      Foo foo = {
31              .buu = buu,
32              .acc = acc
33      };
34      std::cout << "before" << std::endl;
35      printInfo( foo );
36      std::cout << "after" << std::endl;
37  }

我的理解是,clang 假装在 C++ 模式下处理 C99 语法,而不是。


恕我直言,这是一个可以报告给 clang 的错误,因为至少应该根据 1.4 实施合规性发布诊断 [intro.compliance]

1可诊断规则集由本国际标准中的所有句法和语义规则组成,但那些包含“不需要诊断”的明确表示法或被描述为导致“未定义行为”的规则除外。
2 尽管本国际标准仅规定了对 C++ 实现的要求,但如果将这些要求表述为对程序、程序部分或程序执行的要求,则通常更容易理解。此类要求具有以下含义:

  • 如果程序不违反本国际标准中的规则,则符合要求的实现应在其资源限制内接受并正确执行该程序。
  • 如果程序包含违反任何可诊断规则或出现在本标准中描述为“有条件支持”的构造,而实现不支持该构造,则 符合要求的实现应发出至少一个诊断消息

...
8 一个符合要求的实现可以有扩展(包括额外的库函数),只要它们不改变任何格式良好的程序的行为。需要实现来诊断使用根据本国际标准格式错误的扩展的程序。然而,这样做之后,他们可以编译和执行这样的程序。

于 2017-01-18T16:31:55.267 回答