链接的 ostream 内部行为及其在 MSVC 上的结果(与 Clang 相比) [英] Chained ostream internal behavior and their results on MSVC (versus Clang)

查看:47
本文介绍了链接的 ostream 内部行为及其在 MSVC 上的结果(与 Clang 相比)的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

<块引用>

MSVC 与 GCC/Clang 的流、内部字符串和操作顺序问题

大家好,

我最近才开始更认真地使用 MSVC 为我的跨平台项目工作,同时通过 chained STD 流(即一系列 obj.foo() << endl << obj.bar() << endl << [..etc]) 我在使用内部更新时遇到了一个行为string 在 Linux 上使用 GCCClang 时我没想到也没有遇到过.

编译器版本为 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

显然是被倒退了.我必须放弃这个并完成一些有偿工作,但要么我误读了

<块引用>

  1. 在移位运算符表达式E1<E1>>E2 中,E1 的每个值计算和副作用在 E2
  2. 的每个值计算和副作用之前排序

(引用 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

  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) or there's something fishy going on.

这篇关于链接的 ostream 内部行为及其在 MSVC 上的结果(与 Clang 相比)的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆