热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

使用C++11,编写f(x++),g(x++)是不确定的行为?

如何解决《使用C++11,编写f(x++),g(x++)是不确定的行为?》经验,为你挑选了3个好方法。

我正在读这个问题:

未定义的行为和序列点

并且,特别是C++ 11的答案,我理解评估的"排序"的想法.但是 - 写作时是否有足够的顺序:

f(x++), g(x++);

也就是说,我保证f()获得原始值xg()获得一次递增x

挑剔的注意事项:

假设operator++()已定义的行为(即使我们已经重写它),这样做f()g(),没有异常将被抛出,等-这个问题是不是有关.

假设operator,()没有超载.

StoryTeller .. 49

不,行为已定义.引用C++ 11(n3337)[expr.comma/1]:

用逗号分隔的一对表达式从左到右进行评估; 左表达式是一个废弃值表达式(Clause [expr]). 在与右表达式相关联的每个值计算和副作用之前,对与左表达式相关联的每个值计算和副作用进行排序.

我把"每一个"都称为"每一个" 1.x++在呼叫序列f完成并f返回之前,不能进行第二次评估.2


1析构函数调用与子表达式无关,仅与完整表达式相关联.因此,您将看到在完整表达式结束时以相反顺序执行的临时对象创建.
2本段仅适用于作为运营商使用的逗号.当逗号具有特殊含义时(例如在指定函数调用参数序列时),这不适用.



1> StoryTeller ..:

不,行为已定义.引用C++ 11(n3337)[expr.comma/1]:

用逗号分隔的一对表达式从左到右进行评估; 左表达式是一个废弃值表达式(Clause [expr]). 在与右表达式相关联的每个值计算和副作用之前,对与左表达式相关联的每个值计算和副作用进行排序.

我把"每一个"都称为"每一个" 1.x++在呼叫序列f完成并f返回之前,不能进行第二次评估.2


1析构函数调用与子表达式无关,仅与完整表达式相关联.因此,您将看到在完整表达式结束时以相反顺序执行的临时对象创建.
2本段仅适用于作为运营商使用的逗号.当逗号具有特殊含义时(例如在指定函数调用参数序列时),这不适用.


@StoryTeller:对不起,好的,谢谢,这绝对违反直觉:*"销毁临时对象的价值计算和副作用只与全表达有关,而与任何特定的子表达无关."*绝对不是我所期望的从逻辑上讲,析构函数调用表达式*与它相关联.我建议在你的回答中添加析构函数调用,因为它会向那些没有阅读标准的人提出建议......

2> Some program..:

不,它不是未定义的行为.

根据此评估顺序和排序参考,逗号的左侧在右侧之前完全评估(参见规则 9):

9)内置逗号运算符的第一个(左)参数的每个值计算和副作用在每个值计算和第二个(右)参数的副作用之前排序.

这意味着像的表达f(x++), g(x++)未定义.

请注意,这仅对内置逗号运算符有效.


并且,为了强调,这不是C++ 11独有的.自远古以来就是如此.

3> Deduplicator..:
这取决于.

首先,让我们假设它x++本身不会调用未定义的行为.考虑有符号溢出,递增过去的结束指针,或者postfix-increment-operator可能是用户定义的).
此外,让我们假设调用f()g()使用他们的参数并销毁临时对象不会调用未定义的行为.
这是相当多的假设,但如果它们被打破,答案是微不足道的.

现在,如果逗号是内置的逗号操作符,逗号的支撑,初始化列表,或在MEM-初始化列表中的逗号,左侧和右侧分别之前或之后彼此测序(和你知道哪个),所以不要干涉,使行为定义明确.

struct X {
    int f, g;
    explicit X(int x) : f(x++), g(x++) {}
};
// Demonstrate that the order depends on member-order, not initializer-order:
struct Y {
    int g, f;
    explicit Y(int x) : f(x++), g(x++) {}
};
int y[] = { f(x++), g(x++) };

否则,如果x++为postfix-increment调用用户定义的operator-overload,则会对两个实例进行不确定的排序,x++从而对未指定的行为进行排序.

std::list list{1,2,3,4,5,6,7};
auto x = begin(list);
using T = decltype(x);

void h(T, T);
h(f(x++), g(x++));
struct X {
    X(T, T) {}
}
X(f(x++), g(x++));

在最后一种情况下,你得到了完整的未定义行为,因为两个后缀增量x是未经测序的.

int x = 0;

void h(int, int);
h(f(x++), g(x++));
struct X {
    X(int, int) {}
}
X(f(x++), g(x++));


推荐阅读
author-avatar
mobiledu2502861137
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有