问题描述
以下程序的正确行为是什么?
//example.cpp#include <iostream>#include <内存>结构富{无效酒吧()常量{std::cout <<"Foo::Bar()" <<标准::endl;}};std::shared_ptr<Foo>MakeFoo() {返回 std::make_shared<Foo>();}int main() {自动 p { MakeFoo() };p->Bar();}
当我在我的 Linux RHEL 6.6 工作站中编译它时,我得到以下结果:
$ g++ -vgcc 版本 5.1.0 (GCC)$ g++ example.cpp -std=c++14 -Wall -Wextra -pedantic$ ./a.out富::酒吧()
但是
$ clang++ -vclang 3.6.0 版(主干 217965)$ clang++ example.cpp -std=c++14 -Wall -Wextra -pedanticexample.cpp:16:4: 错误:成员引用类型'std::initializer_list<std::shared_ptr<Foo>>'不是指针;也许您打算使用."?p->Bar();~^~example.cpp:16:6: 错误:'std::initializer_list<std::shared_ptr<Foo> 中没有名为 'Bar' 的成员>'p->Bar();~ ^产生 2 个错误.
和
$ icpc -vicpc 15.0.3 版(gcc 5.1.0 版兼容性)$ icpc example.cpp -std=c++14 -Wall -Wextra -pedanticexample.cpp(16):错误:表达式必须具有指针类型p->Bar();^编译中止 example.cpp(代码 2)
Tl;DR
此行为受提案和演进工作组问题的约束.关于这是否被认为是 C++14 缺陷或 C++1z 提议存在一些歧义.如果结果是 C++14 缺陷,那么 gcc 的行为对于 C++14 是正确的.另一方面,如果这确实是一个 C++1z 提案,那么 clang 和 icpc 表现出正确的行为.
详情
N3681 似乎涵盖了这种情况 上面写着:
<块引用>自动和大括号初始化器会导致可教性问题;我们想教人们使用统一初始化,但我们需要特别告诉程序员避免使用 auto 大括号.在 C++14 中,我们现在有更多自动和大括号有问题的情况;返回类型函数的扣除部分避免了这个问题,因为返回花括号列表不起作用,因为它不是表达式.然而,返回从大括号初始化程序初始化的自动变量仍然返回一个初始化列表,邀请未定义的行为.拉姆达初始化捕获有同样的问题.本文建议改变一个大括号初始化的 auto 不推导出初始化器列表,并且在大括号初始化程序具有的情况下禁止大括号初始化自动多个元素.
并提供以下示例:
<块引用>auto x = foo();//复制初始化自动 x{foo};//直接初始化,初始化一个initializer_listint x = foo();//复制初始化诠释 x{foo};//直接初始化
所以我认为目前clang是正确的,最新版本的clang提供了这个警告:
警告:使用推导类型的变量的直接列表初始化将在 Clang 的未来版本中改变含义;插入一个="到避免改变行为 [-Wfuture-compat]
来自 EWG 第 161 期 N3922 是为此采用.
正如 Praetorian 所指出的,该提案建议这是一个 C++14 缺陷:
<块引用>来自 EWG 的指示是,我们认为这是 C++14 中的一个缺陷.
但是 clang 的 C++1z 实现状态说明这是一个 C++1z 提案没有实现.
因此,如果这是 C++14 缺陷,那将使 gcc 正确,但我不清楚这是否真的是缺陷或提议.
T.C.在 评论中指出 它 看起来像铿锵开发人员 确实打算对此进行反向移植.它没有发生,也不清楚为什么.
Which is the correct behaviour for the following program?
// 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();
}
When I compile it in my Linux RHEL 6.6 workstation, I obtain the following results:
$ g++ -v
gcc version 5.1.0 (GCC)
$ g++ example.cpp -std=c++14 -Wall -Wextra -pedantic
$ ./a.out
Foo::Bar()
but
$ 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.
and
$ 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
This behavior is subject to a proposal and an Evolution Working Group issue. There is some ambiguity as to whether this is considered a C++14 defect or a C++1z proposal. If it turns out to be a C++14 defect then gcc's behavior is correct for C++14. On the other hand if this is really a C++1z proposal then clang and icpc are exhibiting correct behavior.
Details
It looks like this case is covered by N3681 which says:
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.
and provides the following examples:
auto x = foo(); // copy-initialization auto x{foo}; // direct-initialization, initializes an initializer_list int x = foo(); // copy-initialization int x{foo}; // direct-initialization
So I think clang is currently correct, the latest version of clang provides this warning:
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]
From EWG issue 161 that N3922 was adopted for this.
As Praetorian notes the proposal recommends this is a C++14 defect:
Direction from EWG is that we consider this a defect in C++14.
but clang's C++1z implementation status notes this as a C++1z proposal which is not implemented.
So if this is a C++14 defect, that would make gcc correct but it is not clear to me if this is really a defect or a proposal.
T.C. points out in a comment here that it seems like the clang developers do intended to back-port this. It has not happened and it is not clear why.
这篇关于表达式的不同编译器行为: auto p {make_pointer()};的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持编程学习网!