链接的 ostream 内部行为及其在 MSVC 上的结果(与 Clang 相比) [英] Chained ostream internal behavior and their results on MSVC (versus Clang)
问题描述
MSVC 与 GCC/Clang 的流、内部字符串和操作顺序问题
大家好,
我最近才开始更认真地使用 MSVC 为我的跨平台项目工作,同时通过 chained STD 流(即一系列 obj.foo() << endl << obj.bar() << endl << [..etc]
) 我在使用内部更新时遇到了一个行为string 在 Linux 上使用 GCC 或 Clang 时我没想到也没有遇到过.
编译器版本为 GCC 7.5、Clang 11.0 和 MSVC 14.0,均启用了 c++17 标准(尽管尚未完成).[编辑:使用 MSVC 16.6.3 的相同问题(编译器内部版本 19.26.28806.0) ]
为了快速理解这里的问题的简化版本:
#include #include #include <字符串>类样本{std::string s;整数 x;上市:样品()=默认;朋友 std::ostream&运算符<<(std::ostream& os, const Sample& a);//更新内部值,返回对象.样本常量&设置(标准::字符串 ss,int xx){s = ss;x = xx;返回 *this;}//更新内部值,返回字符串.std::string const&setStr(std::string ss, int xx) {设置(ss,xx);返回 s;}//更新内部值,返回int.int const&setX(std::string ss, int xx) {设置(ss,xx);返回 x;}};//输出对象整数,行为与字符串相同//或者如果我们刷新内部或任何东西.std::ostream&运算符<<(std::ostream& os, 样本常量& a){操作系统<<斧头;返回操作系统;}int main() {样品一;//GCC/Clang |MSVCstd::cerr <<a.set(0", 0)<<std::endl//0 0<<a.set(1", 1)<
似乎对于字符串或流式输出版本,所有操作都使用相同的最终变异字符串对象,但我不明白为什么会这样(再次,在 GNU/Linux 工具链上没有问题).
我可能会补充一点,如果我们解开流的链接,这个排序问题就会消失:
std::cerr <<a.set(0", 0)<<std::endl;//0";std::cerr <<a.set(1", 1)<
我一开始以为是冲洗问题,但测试显示并非如此.实际上在每个链式调用之间使用 endl
甚至 flush
没有任何作用.
它可能是 Visual-C++ 或什至 CPP101 已知的基本行为(关于内存和诸如此类),但我一无所获,因此我将非常感谢您的任何建议因为在我的书中这很奇怪.
谢谢!
编辑
我已经能够在 GNU/Linux 上重现这个问题(用我的项目,而不是上面的代码)讽刺地试图通过模板可变参数扩展找到替代方案,但这里的事情:
void println()//基础函数{std::cerr <<std::endl;}templateconstexpr void println(T head, Ts... tail){std::cerr <<头<
在 MSVC 上,流似乎像这个后增量可变参数模板一样工作:结果以某种方式向后(或者更像是后递归应用).我不确定这对我有意义.
根据 Microsoft C++ 语言一致性表,C++17 的 更改的评估顺序规则 直到 VS 2017 15.7 才实施.14.0 不够好.您将不得不升级或不链接.
测试
#include 整数 f(){静态 int i = 0;返回 i++;}int main(){std::cout <<f()<<F();}
应该在 C++17 之后产生 01
不打开 C++17 支持(属性->配置属性->语言->C++ 语言标准 = 默认)我得到 10,函数被反向评估.
With Properties->Configuration Properties->Language->C++ Language Standard = ISO C++17 Standard (/std:c++17) 我得到了预期的 01.
但是如果我运行提问者的代码...我仍然看到不正确的响应.删除大部分示例并添加额外的调试行(并将 cerr
替换为 cout
以查看那里是否有一些深奥的魔法)我得到
#include #include #include <字符串>类样本{std::string s;整数 x = 0;上市:样品()=默认;朋友 std::ostream&运算符<<(std::ostream& os, const Sample& a);//更新内部值,返回对象.样本常量&设置(标准::字符串 ss,int xx){std::cout <<"in func 与 "<<ss<
和输出
<前>在 func 中 2在 func 与 1在 func 中为 0000内容 : 0显然是被倒退了.我必须放弃这个并完成一些有偿工作,但要么我误读了
<块引用>- 在移位运算符表达式
E1<
和 E1>>E2
中,E1
的每个值计算和副作用在E2
的每个值计算和副作用之前排序
(引用 cppreference)或者有可疑的事情发生.>
An issue of streams, internal string, and operation ordering with MSVC versus GCC / Clang
Hello everyone,
I just recently began to work more seriously with MSVC for a cross-platform project of mine, and while testing outputs via chained STD stream (ie. a succession of obj.foo() << endl << obj.bar() << endl << [..etc]
) I came across a behavior when using internally updated string neither did I expected nor had encountered on Linux with GCC or Clang.
Compiler versions were GCC 7.5, Clang 11.0 and MSVC 14.0, all with c++17 standard enabled (albeit not completed). [edit: same issue using MSVC 16.6.3 (compiler internal version 19.26.28806.0) ]
For a quick understanding here a simplified version of the issue :
#include <iostream>
#include <ostream>
#include <string>
class Sample {
std::string s;
int x;
public:
Sample() = default;
friend std::ostream& operator<<(std::ostream& os, const Sample& a);
// Update internal value, return the object.
Sample const& set(std::string ss, int xx) {
s = ss;
x = xx;
return *this;
}
// Update internal value, return the string.
std::string const& setStr(std::string ss, int xx) {
set(ss, xx);
return s;
}
// Update internal value, return the int.
int const& setX(std::string ss, int xx) {
set(ss, xx);
return x;
}
};
// Output the object integer, same behavior with the string
// or if we flush inside or anything.
std::ostream& operator<<(std::ostream& os, Sample const& a)
{
os << a.x;
return os;
}
int main() {
Sample a;
// GCC / Clang | MSVC
std::cerr << a.set("0", 0) << std::endl // 0 0
<< a.set("1", 1) << std::endl // 1 0
<< a.set("2", 2) << std::endl; // 2 0
std::cerr << "content : " << a << std::endl; // 2 0
a.set("",-1); std::cerr << std::endl;
std::cerr << a.setStr("0", 0) << std::endl // 0 0
<< a.setStr("1", 1) << std::endl // 1 0
<< a.setStr("2", 2) << std::endl; // 2 0
std::cerr << "content : " << a << std::endl; // 2 0
a.set("",-1); std::cerr << std::endl;
std::cerr << a.setX("0", 0) << std::endl // 0 0
<< a.setX("1", 1) << std::endl // 1 1
<< a.setX("2", 2) << std::endl; // 2 2
std::cerr << "content : " << a << std::endl; // 2 2
}
It appears that with the string or streamed out version all operations use the same final mutated string object, but I can't figure why so (again, with no problem on GNU / Linux toolchains).
I might add that if we unchain the streams this ordering problem disappear :
std::cerr << a.set("0", 0) << std::endl; // "0"
std::cerr << a.set("1", 1) << std::endl; // "1"
std::cerr << a.set("2", 2) << std::endl; // "2"
I first thought it was a flushing problem, but tests shown otherwise. Actually using endl
or even flush
in between each chained call does nothing.
It might be a Visual-C++ or even CPP101 known basic behavior (on memory and whatnot) but I had found nothing about it, so I'll be greatly grateful for any advice you could have as it's pretty darn strange in my book.
Thanks !
Edit
I have been able to reproduce the problem on GNU / Linux (with my project, not the above code) ironically trying to find an alternative via template variadic expansion, but here the things :
void println() // base function
{
std::cerr << std::endl;
}
template<typename T, typename... Ts>
constexpr void println(T head, Ts... tail)
{
std::cerr << head << std::endl;
println(tail...);
}
int main()
{
int i;
i = 0;
println(++i, ++i, ++i); // 3 3 3
i = 0;
println(i++, i++, i++); // 2 1 0
}
On MSVC the stream seem to work like this post-increment variadic template : the results are somehow backward (or more like post recursively applied). I am not sure it made sense to me.
According to the Microsoft C++ language conformance table, C++17's changed evaluation order rules were not implemented until VS 2017 15.7. 14.0 is not good enough. You will have to upgrade or not chain.
Testing
#include <iostream>
int f()
{
static int i = 0;
return i++;
}
int main()
{
std::cout << f() << f();
}
Should produce 01 after C++17
Without turning on C++17 support (Properties->Configuration Properties->Language->C++ Language Standard = default) I get 10, the functions are evaluated in reverse.
With Properties->Configuration Properties->Language->C++ Language Standard = ISO C++17 Standard (/std:c++17) I get the expected 01.
But if I run the asker's code... I still see the incorrect response. Removing most of the example and adding in an extra debug line (and replacing cerr
with cout
to see if there's some deep magic there) I get
#include <iostream>
#include <ostream>
#include <string>
class Sample {
std::string s;
int x = 0;
public:
Sample() = default;
friend std::ostream& operator<<(std::ostream& os, const Sample& a);
// Update internal value, return the object.
Sample const& set(std::string ss, int xx) {
std::cout << "in func with " << ss << std::endl;
s = ss;
x = xx;
return *this;
}
};
// Output the object integer, same behavior with the string
// or if we flush inside or anything.
std::ostream& operator<<(std::ostream& os, Sample const& a)
{
os << a.x;
return os;
}
int main() {
Sample a;
// GCC / Clang | MSVC
std::cout << a.set("0", 0) << std::endl // 0 0
<< a.set("1", 1) << std::endl // 1 0
<< a.set("2", 2) << std::endl; // 2 0
std::cout << "content : " << a << std::endl; // 2 0
}
and output
in func with 2 in func with 1 in func with 0 0 0 0 content : 0
Clearly being called backwards. I have to ditch this and get some paid work done, but either I'm misreading
- In a shift operator expression
E1<<E2
andE1>>E2
, every value computation and side-effect ofE1
is sequenced before every value computation and side effect ofE2
(Quoting cppreference) or there's something fishy going on.
这篇关于链接的 ostream 内部行为及其在 MSVC 上的结果(与 Clang 相比)的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!