将此主题视为以下主题的续篇:
上一部分
未定义的行为和序列点
让我们重新审视这个有趣而复杂的表达(斜体短语取自上述主题*smile*):
i += ++i;
Run Code Online (Sandbox Code Playgroud)
我们说这会调用undefined-behavior.我假定说这个的时候,我们隐含假设型的i是内置的类型之一.
如果什么类型的i是用户定义类型?比如它的类型是Index在本文后面定义的(见下文).它还会调用未定义的行为吗?
如果是,为什么?它不等同于写作i.operator+=(i.operator++());甚至语法上更简单 i.add(i.inc());吗?或者,他们是否也调用未定义的行为?
如果不是,为什么不呢?毕竟,对象在连续的序列点之间i被修改两次.请回想一下经验法则:表达式只能在连续的"序列点"之间修改一个对象的值.如果 i += ++i是表达式,那么它必须调用未定义的行为.如果是,那么它的等价物i.operator+=(i.operator++());也 i.add(i.inc());必须调用undefined-behavior似乎是不真实的!(据我所知)
或者,i += ++i不是一个开头的表达?如果是这样,那么它是什么以及表达式的定义是什么?
如果它是一个表达式,并在同一时间,其行为也是定义良好的,那么就意味着与表达相关序列点的数量在某种程度上取决于该类型的参与表达操作数.我是否正确(甚至部分)?
顺便问一下,这个表达怎么样?
//Consider two cases:
//1. If a is an array of a built-in type
//2. If a is user-defined type which overloads the …Run Code Online (Sandbox Code Playgroud) 在典型的C++代码中,C++ 17评估顺序保证(P0145)投票的含义是什么?
对于像这样的事情,它有什么变化
i=1;
f(i++, i)
Run Code Online (Sandbox Code Playgroud)
和
std::cout << f() << f() << f() ;
Run Code Online (Sandbox Code Playgroud)
要么
f(g(),h(),j());
Run Code Online (Sandbox Code Playgroud) 关于以下代码是否是合法的C++ ,这个问题一直存在争议:
std::list<item*>::iterator i = items.begin();
while (i != items.end())
{
bool isActive = (*i)->update();
if (!isActive)
{
items.erase(i++); // *** Is this undefined behavior? ***
}
else
{
other_code_involving(*i);
++i;
}
}
Run Code Online (Sandbox Code Playgroud)
这里的问题是erase()将使有问题的迭代器无效.如果在i++评估之前发生这种情况,那么这样的增量i在技术上是未定义的行为,即使它似乎与特定的编译器一起使用.辩论的一方面说,在调用函数之前,所有函数参数都已完全评估.另一方说,"唯一的保证是i ++将在下一个语句之前和使用i ++之后发生.无论是在擦除(i ++)之前还是之后都依赖于编译器."
我打开这个问题,希望能够解决这个问题.
我有一个函数,它将ostream引用作为参数,将一些数据写入流,然后返回对同一个流的引用,如下所示:
#include <iostream>
std::ostream& print( std::ostream& os ) {
os << " How are you?" << std::endl;
return os;
}
int main() {
std::cout << "Hello, world!" << print( std::cout ) << std::endl;
}
Run Code Online (Sandbox Code Playgroud)
此代码的输出是:
How are you?
Hello, world!0x601288
Run Code Online (Sandbox Code Playgroud)
但是,如果我将链接表达式分成两个语句,就像这样
int main() {
std::cout << "Hello, world!";
std::cout << print( std::cout ) << std::endl;
}
Run Code Online (Sandbox Code Playgroud)
然后我至少在输出中得到正确的顺序,但仍然得到一个十六进制值:
Hello, world! How are you?
0x600ec8
Run Code Online (Sandbox Code Playgroud)
我想了解这里发生了什么.正常函数是否优先operator<<,这就是输出顺序反转的原因?编写将数据插入到一个ostream但也可以链接的函数的正确方法是什么operator<<?
我一直在尝试使用函数,我发现参数的顺序在内存中是相反的.这是为什么?
堆栈TEST.CPP:
#include <stdio.h>
void test( int a, int b, int c ) {
printf("%p %p %p\n", &a, &b, &c);
printf("%d %d\n", *(&b - 1), *(&b + 1) );
}
int main() {
test(1,2,3);
return 0;
}
Run Code Online (Sandbox Code Playgroud)
铛:
$ clang++ stack-test.cpp && ./a.out
0x7fffb9bb816c 0x7fffb9bb8168 0x7fffb9bb8164
3 1
Run Code Online (Sandbox Code Playgroud)
GCC:
$ g++ stack-test.cpp && ./a.out
0x7ffe0b983b3c 0x7ffe0b983b38 0x7ffe0b983b34
3 1
Run Code Online (Sandbox Code Playgroud)
编辑:不重复:评估顺序可能与内存布局不同,所以这是一个不同的问题.
众所周知,函数参数的评估顺序未指定,并且可能因不同的编译器而不同.
似乎不太清楚的是函数调用是否可以交错,具体如下:
f(g(h()), i(j()))
Run Code Online (Sandbox Code Playgroud)
让我们假设编译器首先选择评估f的第一个参数.是编译器来电j通话之间h和g?我相信,但我不知道在标准中哪里可以找到确认.
可能重复:
在C中调用函数之前的参数评估顺序
对于下面的代码,我预计输出为20和76,而75和21将作为输出.请解释为什么会这样.
#include<stdio.h>
unsigned func(unsigned n)
{
unsigned int a =1 ;
static unsigned int b=2;
a+=b; b+=a;
{
unsigned int a=3;
a+=b; b+=a;
}
//printf("%d %d ",a,b);
return (n+a+b);
}
int main()
{
printf("%d %d\n",func(4),func(5));
return 0;
}
Run Code Online (Sandbox Code Playgroud) 我在看“如何正确使用可变参数模板的引用”,并想知道逗号扩展可以走多远。
这是答案的一个变体:
inline void inc() { }
template<typename T,typename ...Args>
inline void inc(T& t, Args& ...args) { ++t; inc(args...); }
Run Code Online (Sandbox Code Playgroud)
由于可变参数被扩展到一逗号-分隔它们的元素的列表,是那些逗号语义上等同于模板/功能参数的分离器,或者他们插入词法,使得它们适用于任何(-预处理后)的使用,包括逗号操作符?
这适用于我的 GCC-4.6:
// Use the same zero-argument "inc"
template<typename T,typename ...Args>
inline void inc(T& t, Args& ...args) { ++t, inc(args...); }
Run Code Online (Sandbox Code Playgroud)
但是当我尝试时:
// Use the same zero-argument "inc"
template<typename T,typename ...Args>
inline void inc(T& t, Args& ...args) { ++t, ++args...; }
Run Code Online (Sandbox Code Playgroud)
我不断收到解析错误,期待“;” 在“...”之前,并且“args”不会扩展其包。为什么不起作用?是因为如果“args”为空,我们会得到一个无效的标点符号?合法吗,我的编译器不够好?
(我试过在括号中围绕“args”,和/或使用后增量;都没有奏效。)
我读过这篇文章:未定义的行为和序列点,但我不知道,是否是UB.
请考虑以下示例:
#include <iostream>
class op {
public:
explicit op(int x) {
std::cout << "x: " << x << std::endl;
}
op & operator + (const op & /* other */) {
return *this;
}
};
int main(int /* argc */, char * /* argv */ []) {
int x = 0;
op o = op(x++) + op(x++) + op(x++);
std::cout << "res: " << x << std::endl;
return 0;
}
Run Code Online (Sandbox Code Playgroud)
我希望这样的输出(或基于评估顺序的输出的一些排列):
x: 0
x: 1
x: 2 …Run Code Online (Sandbox Code Playgroud) 为什么g()首先调用函数?我定义g()为初始化列表中的第二个元素。
以下与初始值设定项列表相关的标准引用是否相关?
\n\n\n\n\n\xc2\xa78.5.4.4:在花括号初始化列表的初始值设定项列表中,\n 初始值设定项子句,包括任何由包扩展产生的\n (\xc2\xa714.5.3),在它们出现的顺序。
\n
#include <iostream>\n#include <vector>\n\nint f() { std::cout << "f"; return 0;}\nint g() { std::cout << "g"; return 0;}\n\nvoid h(std::vector<int> v) {}\n\nint main() {\n\n h({f(), g()});\n}\nRun Code Online (Sandbox Code Playgroud)\n\n输出:
\n\ngf\nRun Code Online (Sandbox Code Playgroud)\n