表达式的不同编译器行为:auto p {make_pointer()};

Esc*_*alo 15 c++ g++ icc clang++ c++14

以下程序的正确行为是什么?

// example.cpp

#include <iostream>
#include <memory>

struct Foo {
  void Bar() const {
    std::cout << "Foo::Bar()" << std::endl;
  }
};

std::shared_ptr<Foo> MakeFoo() {
  return std::make_shared<Foo>();
}

int main() {
  auto p { MakeFoo() };
  p->Bar();  
}
Run Code Online (Sandbox Code Playgroud)

当我在Linux RHEL 6.6工作站中编译它时,我得到以下结果:

$ g++ -v
gcc version 5.1.0 (GCC)
$ g++ example.cpp -std=c++14 -Wall -Wextra -pedantic
$ ./a.out
Foo::Bar()
Run Code Online (Sandbox Code Playgroud)

$ clang++ -v
clang version 3.6.0 (trunk 217965)
$ clang++ example.cpp -std=c++14 -Wall -Wextra -pedantic
example.cpp:16:4: error: member reference type 'std::initializer_list<std::shared_ptr<Foo> >' is not a pointer; maybe you meant to use '.'?
      p->Bar();
      ~^~
example.cpp:16:6: error: no member named 'Bar' in 'std::initializer_list<std::shared_ptr<Foo> >'
      p->Bar();
      ~  ^
    2 errors generated.
Run Code Online (Sandbox Code Playgroud)

$ icpc -v
icpc version 15.0.3 (gcc version 5.1.0 compatibility)
$ icpc example.cpp -std=c++14 -Wall -Wextra -pedantic
example.cpp(16): error: expression must have pointer type
    p->Bar();
    ^
compilation aborted for example.cpp (code 2)
Run Code Online (Sandbox Code Playgroud)

Sha*_*our 16

TL; DR

此行为受提案和Evolution工作组问题的影响.关于这是否被认为是C++ 14缺陷或C++ 1z提案存在一些含糊之处.如果结果是C++ 14缺陷那么gcc的行为对于C++ 14是正确的.另一方面,如果这真的是一个C++ 1z提案,那么clang和icpc表现出正确的行为.

细节

看起来N3681涵盖了这种情况,它说:

自动和支撑的初始化器会导致可教性问题; 我们想教人们使用统一初始化,但我们需要专门告诉程序员避免使用auto进行大括号.在C++ 14中,我们现在有更多的情况,其中auto和braces有问题; 函数的返回类型推导部分避免了问题,因为返回一个支撑列表将不起作用,因为它不是一个表达式.但是,返回从支撑初始化程序初始化的自动变量仍会返回initializer_list,从而引发未定义的行为.Lambda init捕获具有相同的问题.本文提出将括号初始化auto更改为不推导到初始化列表,并在braced-initializer具有多个元素的情况下禁用大括号初始化auto.

并提供以下示例:

auto x = foo(); // copy-initialization
auto x{foo}; // direct-initialization, initializes an initializer_list
int x = foo(); // copy-initialization
int x{foo}; // direct-initialization
Run Code Online (Sandbox Code Playgroud)

所以我认为clang目前是正确的, 最新版本的clang提供了这个警告:

警告:使用推导类型的变量的直接列表初始化将改变Clang的未来版本中的含义; 插入'='以避免行为发生变化[-Wfuture-compat]

EWG第161期开始,N3922就是为此而采用的.

正如Praetorian指出,该提案建议这是一个C++ 14缺陷:

EWG的方向是我们认为这是C++ 14中的一个缺陷.

但clang的C++ 1z实现状态指出这是一个未实现的C++ 1z提议.

因此,如果这是一个C++ 14缺陷,那将使gcc正确,但我不清楚这是否真的是一个缺陷或提案.

TC在评论中指出,似乎clang开发人员确实想要支持这一点.它没有发生,也不清楚为什么.

  • Scoot Myers在大约30分钟后谈到这里[这里](https://channel9.msdn.com/Events/CPP/C-PP-Con-2014/Type-Deduction-and-Why-You-Care). (4认同)