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

Different compiler behavior for expression: auto p {make_pointer()};

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

// 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();  
}

当我在我的 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()

但是

$ 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.

$ 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)

Tl;DR

此行为受提案和 Evolution 工作组问题的约束。关于将其视为 C++14 缺陷还是 C++1z 提议存在一些歧义。如果证明是 C++14 缺陷,那么 gcc 的行为对于 C++14 是正确的。另一方面,如果这真的是一个 C++1z 提议,那么 clang 和 icpc 就会表现出正确的行为。

详情

N3681 似乎涵盖了这种情况,它说:

Auto and braced initializers cause a teachability problem; we want to teach people to use uniform initialization, but we need to specifically tell programmers to avoid braces with auto. In C++14, we now have more cases where auto and braces are problematic; return type deduction for functions partially avoids the problem, since returning a braced-list won't work as it's not an expression. However, returning an auto variable initialized from a braced initializer still returns an initializer_list, inviting undefined behaviour. Lambda init captures have the same problem. This paper proposes to change a brace-initialized auto to not deduce to an initializer list, and to ban brace-initialized auto for cases where the braced-initializer has more than one element.

并提供了以下示例:

auto x = foo(); // copy-initialization
auto x{foo}; // direct-initialization, initializes an initializer_list
int x = foo(); // copy-initialization
int x{foo}; // direct-initialization

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

warning: direct list initialization of a variable with a deduced type will change meaning in a future version of Clang; insert an '=' to avoid a change in behavior [-Wfuture-compat]

来自 EWG issue 161 that N3922 为此被采用。

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

Direction from EWG is that we consider this a defect in C++14.

但是 clang 的 C++1z implementation status notes 这是一个未实现的 C++1z 提案。

因此,如果这是一个 C++14 缺陷,那将使 gcc 正确,但我不清楚这到底是一个缺陷还是一个提案。

T.C。在 that it seems like the clang developers 中指出确实打算向后移植这个。它并没有发生,也不清楚为什么。