找回密码
 欢迎注册
楼主: gxqcn

[分享] 发现微软编译器三路比较运算(<=>)的一个大 bug!

[复制链接]
 楼主| 发表于 2020-3-28 15:03:04 | 显示全部楼层
为了检验 std::max( a++, b++ ) 函数是否也有类似问题,我把代码加入了 1 楼的 test 函数中,
发现 std::max( a++, b++ ) 的结果符合预期。
所以,现在的“a++ <=> b++”的调用若有附加要求,是不合理的,虽然很容易避开;但很容易误闯。
毋因群疑而阻独见  毋任己意而废人言
毋私小惠而伤大体  毋借公论以快私情
发表于 2020-3-29 13:39:49 | 显示全部楼层
搜了下,  发现 三路运算符的引入并不是编译器做简单的功能上的加法.  
其实是对编译器 关于比较操作符处理的 一次全面革新,   https://en.wikipedia.org/wiki/Trichotomy_(mathematics)

三分律: 对于任意的一个实数$x$,一定 有且仅有一种描述成立, $x<0, x=0,x>0$
于是,编译器对所有的比较运算都进行重构了, 比如 $a>b$ 用三路运算符表达就是  $a<=>b >0$


C++20开始, 编译器会为类产生一个默认的 比较成员函数. https://en.cppreference.com/w/cpp/language/default_comparisons


以前要实现一个类的比较操作,需要很繁琐的分别实现 $<,<=,>,>=,=,!=$这六种操作符.
  1. class CIString {
  2.   string s;

  3. public:
  4.   friend bool operator==(const CIString& a, const CIString& b) {
  5.     return a.s.size() == b.s.size() &&
  6.       ci_compare(a.s.c_str(), b.s.c_str()) == 0;
  7.   }
  8.   friend bool operator< (const CIString& a, const CIString& b) {
  9.     return ci_compare(a.s.c_str(), b.s.c_str()) <  0;
  10.   }
  11.   friend bool operator!=(const CIString& a, const CIString& b) {
  12.     return !(a == b);
  13.   }
  14.   friend bool operator> (const CIString& a, const CIString& b) {
  15.     return b < a;
  16.   }
  17.   friend bool operator>=(const CIString& a, const CIString& b) {
  18.     return !(a < b);
  19.   }
  20.   friend bool operator<=(const CIString& a, const CIString& b) {
  21.     return !(b < a);
  22.   }

  23.   friend bool operator==(const CIString& a, const char* b) {
  24.     return ci_compare(a.s.c_str(), b) == 0;
  25.   }
  26.   friend bool operator< (const CIString& a, const char* b) {
  27.     return ci_compare(a.s.c_str(), b) <  0;
  28.   }
  29.   friend bool operator!=(const CIString& a, const char* b) {
  30.     return !(a == b);
  31.   }
  32.   friend bool operator> (const CIString& a, const char* b) {
  33.     return b < a;
  34.   }
  35.   friend bool operator>=(const CIString& a, const char* b) {
  36.     return !(a < b);
  37.   }
  38.   friend bool operator<=(const CIString& a, const char* b) {
  39.     return !(b < a);
  40.   }

  41.   friend bool operator==(const char* a, const CIString& b) {
  42.     return ci_compare(a, b.s.c_str()) == 0;
  43.   }
  44.   friend bool operator< (const char* a, const CIString& b) {
  45.     return ci_compare(a, b.s.c_str()) <  0;
  46.   }
  47.   friend bool operator!=(const char* a, const CIString& b) {
  48.     return !(a == b);
  49.   }
  50.   friend bool operator> (const char* a, const CIString& b) {
  51.     return b < a;
  52.   }
  53.   friend bool operator>=(const char* a, const CIString& b) {
  54.     return !(a < b);
  55.   }
  56.   friend bool operator<=(const char* a, const CIString& b) {
  57.     return !(b < a);
  58.   }
  59. };
复制代码


用了三路运算符之后,就简单了,

  1. class CIString {
  2.   string s;

  3. public:
  4.   bool operator==(const CIString& b) const {
  5.     return s.size() == b.s.size() &&
  6.       ci_compare(s.c_str(), b.s.c_str()) == 0;
  7.   }
  8.   std::weak_ordering operator<=>(const CIString& b) const {
  9.     return ci_compare(s.c_str(), b.s.c_str()) <=> 0;
  10.   }

  11.   bool operator==(char const* b) const {
  12.     return ci_compare(s.c_str(), b) == 0;
  13.   }
  14.   std::weak_ordering operator<=>(const char* b) const {
  15.     return ci_compare(s.c_str(), b) <=> 0;
  16.   }
  17. };
复制代码

https://brevzin.github.io/c++/2019/07/28/comparisons-cpp20/


毋因群疑而阻独见  毋任己意而废人言
毋私小惠而伤大体  毋借公论以快私情
 楼主| 发表于 2020-3-29 14:26:48 | 显示全部楼层
这就是新标准的魅力:现在可仅自定义 <=>,== 两种比较运算符,就可以自动实现之前的全部六种比较运算符的功能
毋因群疑而阻独见  毋任己意而废人言
毋私小惠而伤大体  毋借公论以快私情
发表于 2020-6-1 22:59:20 | 显示全部楼层
今天更新了下ArchLinux系统,gcc升级到了10.1.0版本, 跑了下老大的代码(只是去掉了原版本的第一行和最后一行)。发现bug 不复存在,以前的gcc是编译不通过。
另外,clang 10.0.0也全面支持,此bug不存在。符合https://zh.cppreference.com/w/cpp/compiler_support 里的支持声明。

  1. // test.cpp : 此文件包含 "main" 函数。程序执行将在此处开始并结束。
  2. //
  3. #include <compare>
  4. #include <iostream>

  5. // 2020-03-16, gxqcn 发现编译器 bug: 如写成 const auto cmp{ *--pl <=> *--pr }, 当 0 != cmp 时,指针会多移动一次!
  6. int main()
  7. {
  8.     const auto test{ []( auto a, auto b )
  9.     {
  10.         std::cout << "before compare: a=" << a << " b=" << b << "\n";
  11.         const auto cmp{ a++ <=> b++ };
  12.         std::cout << "after  compare: a=" << a << " b=" << b << "\n\n";

  13.         // 为了对比检测 std::max() 是否有类似表现,
  14.         std::cout << "std::max( a++, b++ ) = " << std::max( a++, b++ ) << "\n";
  15.         std::cout << "after  std::max: a=" << a << " b=" << b << "\n\n";
  16.     }};

  17.     test( 1, 1 );   // 测试表明:当相等时,表现与预期相符
  18.     test( 1, 2 );   // 测试表明:当不等时,表现与预期不符

  19.     std::cout << "-------------------------\n\n";

  20.     int v1[]{ 1, 2, 3, 4, 5, 6 };
  21.     int v2[]{ 1, 2, 0, 9, 0, 6 };

  22.     auto p1{ std::cbegin( v1 ) - 1 };
  23.     auto p2{ std::cbegin( v2 ) - 1 };

  24.     std::cout << "compare: v1{ 1, 2, 3, 4, 5, 6 } <=> v2{ 1, 2, 0, 9, 0, 6 }\n\n";

  25.     for ( auto index{ 0 }; sizeof( v1 ) / sizeof( v1[ 0 ] ) != index; ++index )
  26.     {
  27.         if ( const auto cmp{ *++p1 <=> *++p2 }; 0 != cmp )
  28.         {
  29.             std::cout << "v1[" << index << "] = " << v1[ index ] << "; *p1 =" << *p1 << "\n";
  30.             std::cout << "v2[" << index << "] = " << v2[ index ] << "; *p2 =" << *p2 << "\n";

  31.             if ( 0 < cmp )
  32.             {
  33.                 std::cout << "\nv1 > v2";
  34.             }
  35.             else
  36.             {
  37.                 std::cout << "\nv1 < v2";
  38.             }

  39.             if (( v1[ index ] <=> v2[ index ] ) == cmp )
  40.             {
  41.                 std::cout << ", it's right.\n\n";
  42.             }
  43.             else
  44.             {
  45.                 std::cout << " ?! it's wrong!!\n\n";
  46.             }

  47.             break;
  48.         }
  49.     }
  50. }
复制代码

运行结果:
  1. before compare: a=1 b=1
  2. after  compare: a=2 b=2

  3. std::max( a++, b++ ) = 2
  4. after  std::max: a=3 b=3

  5. before compare: a=1 b=2
  6. after  compare: a=2 b=3

  7. std::max( a++, b++ ) = 3
  8. after  std::max: a=3 b=4

  9. -------------------------

  10. compare: v1{ 1, 2, 3, 4, 5, 6 } <=> v2{ 1, 2, 0, 9, 0, 6 }

  11. v1[2] = 3; *p1 =3
  12. v2[2] = 0; *p2 =0

  13. v1 > v2, it's right.
复制代码

毋因群疑而阻独见  毋任己意而废人言
毋私小惠而伤大体  毋借公论以快私情
发表于 2020-6-1 23:10:15 | 显示全部楼层
在 gcc官网也明确看到了gcc 10的支持清单https://gcc.gnu.org/gcc-10/changes.html
Runtime Library (libstdc++)
    Improved experimental C++2a support, including:
        Library concepts in <concepts> and <iterator>.
        Constrained algorithms in <ranges>, <algorithm>, and <memory> (thanks to Patrick Palka).
        New algorithms shift_left and shift_right (thanks to Patrick Palka).
        std::span (thanks to JeanHeyd Meneide).
        Three-way comparisons in <compare> and throughout the library.        
        Constexpr support in <algorithm> and elsewhere (thanks to Edward Smith-Rowland).
        <stop_token> and std::jthread (thanks to Thomas Rodgers).
        std::atomic_ref and std::atomic<floating point>.
        Integer comparison functions (cmp_equal, cmp_less etc.).
        std::ssize, std::to_array.
        std::construct_at, std::destroy, constexpr std::allocator.
        Mathematical constants in <numbers>.
    Support for RDSEED in std::random_device.
    Reduced header dependencies, leading to faster compilation for some code.
毋因群疑而阻独见  毋任己意而废人言
毋私小惠而伤大体  毋借公论以快私情
 楼主| 发表于 2020-6-2 09:02:51 | 显示全部楼层
刚测了下,在最新的 VS2019 16.6.0 上,bug 依旧存在。。。

点评

clang 10.0.0 也没问题  发表于 2020-6-2 11:00
毋因群疑而阻独见  毋任己意而废人言
毋私小惠而伤大体  毋借公论以快私情
发表于 2020-6-2 21:11:05 | 显示全部楼层
我把这个测试代码提交到了微软,希望能修复。

点评

赞  发表于 2020-6-2 23:49
毋因群疑而阻独见  毋任己意而废人言
毋私小惠而伤大体  毋借公论以快私情
发表于 2020-11-11 16:31:25 | 显示全部楼层
今天收到微软回复,说这个bug已经解决,VS2019 16.8版本。不过我还没收到升级推送。

评分

参与人数 1威望 +12 金币 +12 贡献 +12 鲜花 +12 收起 理由
gxqcn + 12 + 12 + 12 + 12 这也算是我们论坛为业界做的一份贡献

查看全部评分

毋因群疑而阻独见  毋任己意而废人言
毋私小惠而伤大体  毋借公论以快私情
 楼主| 发表于 2020-11-11 22:20:26 | 显示全部楼层
看到上面的提示,VS2019 16.8.0 已可更新,更新安装好再编译,测试通过了。
毋因群疑而阻独见  毋任己意而废人言
毋私小惠而伤大体  毋借公论以快私情
发表于 2020-11-12 08:18:21 | 显示全部楼层
gxqcn 发表于 2020-11-11 22:20
看到上面的提示,VS2019 16.8.0 已可更新,更新安装好再编译,测试通过了。

写程序要保证编译器的稳定,这样写出来的程序才是稳定的,
我很有这个感受,有时候写的代码,运行软件的版本变了,结果就不一样了,
所以写代码:稳定压倒一切!
毋因群疑而阻独见  毋任己意而废人言
毋私小惠而伤大体  毋借公论以快私情
您需要登录后才可以回帖 登录 | 欢迎注册

本版积分规则

小黑屋|手机版|数学研发网 ( 苏ICP备07505100号 )

GMT+8, 2024-4-23 23:03 , Processed in 0.069894 second(s), 15 queries .

Powered by Discuz! X3.5

© 2001-2024 Discuz! Team.

快速回复 返回顶部 返回列表