gxqcn 发表于 2020-3-28 15:03:04

为了检验 std::max( a++, b++ ) 函数是否也有类似问题,我把代码加入了 1 楼的 test 函数中,
发现 std::max( a++, b++ ) 的结果符合预期。
所以,现在的“a++ <=> b++”的调用若有附加要求,是不合理的,虽然很容易避开;但很容易误闯。

wayne 发表于 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


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

public:
friend bool operator==(const CIString& a, const CIString& b) {
    return a.s.size() == b.s.size() &&
      ci_compare(a.s.c_str(), b.s.c_str()) == 0;
}
friend bool operator< (const CIString& a, const CIString& b) {
    return ci_compare(a.s.c_str(), b.s.c_str()) <0;
}
friend bool operator!=(const CIString& a, const CIString& b) {
    return !(a == b);
}
friend bool operator> (const CIString& a, const CIString& b) {
    return b < a;
}
friend bool operator>=(const CIString& a, const CIString& b) {
    return !(a < b);
}
friend bool operator<=(const CIString& a, const CIString& b) {
    return !(b < a);
}

friend bool operator==(const CIString& a, const char* b) {
    return ci_compare(a.s.c_str(), b) == 0;
}
friend bool operator< (const CIString& a, const char* b) {
    return ci_compare(a.s.c_str(), b) <0;
}
friend bool operator!=(const CIString& a, const char* b) {
    return !(a == b);
}
friend bool operator> (const CIString& a, const char* b) {
    return b < a;
}
friend bool operator>=(const CIString& a, const char* b) {
    return !(a < b);
}
friend bool operator<=(const CIString& a, const char* b) {
    return !(b < a);
}

friend bool operator==(const char* a, const CIString& b) {
    return ci_compare(a, b.s.c_str()) == 0;
}
friend bool operator< (const char* a, const CIString& b) {
    return ci_compare(a, b.s.c_str()) <0;
}
friend bool operator!=(const char* a, const CIString& b) {
    return !(a == b);
}
friend bool operator> (const char* a, const CIString& b) {
    return b < a;
}
friend bool operator>=(const char* a, const CIString& b) {
    return !(a < b);
}
friend bool operator<=(const char* a, const CIString& b) {
    return !(b < a);
}
};

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

class CIString {
string s;

public:
bool operator==(const CIString& b) const {
    return s.size() == b.s.size() &&
      ci_compare(s.c_str(), b.s.c_str()) == 0;
}
std::weak_ordering operator<=>(const CIString& b) const {
    return ci_compare(s.c_str(), b.s.c_str()) <=> 0;
}

bool operator==(char const* b) const {
    return ci_compare(s.c_str(), b) == 0;
}
std::weak_ordering operator<=>(const char* b) const {
    return ci_compare(s.c_str(), b) <=> 0;
}
};
https://brevzin.github.io/c++/2019/07/28/comparisons-cpp20/


gxqcn 发表于 2020-3-29 14:26:48

这就是新标准的魅力:现在可仅自定义 <=>,== 两种比较运算符,就可以自动实现之前的全部六种比较运算符的功能

wayne 发表于 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 里的支持声明。

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

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

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

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

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

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

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

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

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

            if ( 0 < cmp )
            {
                std::cout << "\nv1 > v2";
            }
            else
            {
                std::cout << "\nv1 < v2";
            }

            if (( v1[ index ] <=> v2[ index ] ) == cmp )
            {
                std::cout << ", it's right.\n\n";
            }
            else
            {
                std::cout << " ?! it's wrong!!\n\n";
            }

            break;
      }
    }
}

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

std::max( a++, b++ ) = 2
afterstd::max: a=3 b=3

before compare: a=1 b=2
aftercompare: a=2 b=3

std::max( a++, b++ ) = 3
afterstd::max: a=3 b=4

-------------------------

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

v1 = 3; *p1 =3
v2 = 0; *p2 =0

v1 > v2, it's right.

wayne 发表于 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.

gxqcn 发表于 2020-6-2 09:02:51

刚测了下,在最新的 VS2019 16.6.0 上,bug 依旧存在。。。

风云剑 发表于 2020-6-2 21:11:05

我把这个测试代码提交到了微软,希望能修复。

风云剑 发表于 2020-11-11 16:31:25

今天收到微软回复,说这个bug已经解决,VS2019 16.8版本。不过我还没收到升级推送。

gxqcn 发表于 2020-11-11 22:20:26

看到上面的提示,VS2019 16.8.0 已可更新,更新安装好再编译,测试通过了。:)

mathematica 发表于 2020-11-12 08:18:21

gxqcn 发表于 2020-11-11 22:20
看到上面的提示,VS2019 16.8.0 已可更新,更新安装好再编译,测试通过了。

写程序要保证编译器的稳定,这样写出来的程序才是稳定的,
我很有这个感受,有时候写的代码,运行软件的版本变了,结果就不一样了,
所以写代码:稳定压倒一切!
页: 1 2 [3] 4
查看完整版本: 发现微软编译器三路比较运算(<=>)的一个大 bug!