gpt4 book ai didi

c++ - 链式ostream内部行为及其在MSVC上的结果(与Clang相比)

转载 作者:行者123 更新时间:2023-12-01 14:52:08 24 4
gpt4 key购买 nike

An issue of streams, internal string, and operation ordering with MSVC versus GCC / Clang


大家好,
最近,我刚刚开始与MSVC一起为我的跨平台项目更加认真地工作,并且在通过链式STD流(即一串 obj.foo() << endl << obj.bar() << endl << [..etc])测试输出时,遇到了使用内部更新的字符串时既不期望也不期望的行为。在Linux上使用GCC或Clang遇到过。
编译器版本为GCC 7.5,Clang 11.0和MSVC 14.0,所有版本都启用了c++ 17标准(尽管尚未完成)。 [ 编辑:使用MSVC 16.6.3的相同问题(编译器内部版本19.26.28806.0)]
为了快速了解此处,此问题的简化版本:
#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
}
看起来,使用字符串或流版本,所有操作都使用相同的最终变异的字符串对象,但我不知道为什么这样做(再次,在GNU / Linux工具链上没有问题)。
我可能还要补充一点,如果我们取消串流,则此排序问题将消失:
    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"
我首先认为这是一个冲洗问题,但测试显示为其他情况。实际上,在每个链接的调用之间使用 endl甚至 flush都没有任何作用。
它可能是Visual-C++甚至CPP101已知的基本行为(在内存上,什么都没有),但是我什么都没找到,所以我将非常感谢您可能提出的任何建议,因为这在我的书中非常奇怪。
谢谢 !
编辑
我已经能够在GNU / Linux上重现该问题(使用我的项目,而不是上面的代码),具有讽刺意味的是,尝试通过模板可变参数扩展找到替代方法,但是这里的事情是:
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
}
在MSVC上,流似乎像此后增量可变参数模板一样工作:结果以某种方式向后(或更像递归地应用后)。我不确定这对我是否有意义。

最佳答案

根据Microsoft C++ language conformance table,直到VS 2017 15.7才实现C++ 17的changed evaluation order rules。 14.0不够好。您将不得不升级或不升级。
测验

#include <iostream>


int f()
{
static int i = 0;
return i++;
}

int main()
{
std::cout << f() << f();
}
应该在C++ 17之后产生01
如果不打开C++ 17支持(属性->配置属性->语言-> C++语言标准=默认值),我得到10,这些函数的求值顺序相反。
使用Properties-> Configuration Properties-> Language-> C++ Language Standard = ISO C++ 17 Standard(/ std:c++ 17)我得到了预期的01。
但是,如果我运行询问者的代码...,我仍然看到错误的响应。删除了大部分示例,并添加了一条额外的调试行(并将 cerr替换为 cout以查看其中是否存在一些深奥的魔力),我得到了
#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
}
和输出
与2
与1
在功能与0
0
0
0
内容:0

显然被称为倒退。我必须放弃这一点,并完成一些有偿工作,但是我读错了
  1. In a shift operator expression E1<<E2 and E1>>E2, every value computation and side-effect of E1 is sequenced before every value computation and side effect of E2

( Quoting cppreference)或发生了一些麻烦。

关于c++ - 链式ostream内部行为及其在MSVC上的结果(与Clang相比),我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/62666619/

24 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com