让我感到惊讶的是,我遇到了另一个问题,类似于 C++20行为破坏现有代码等于运算符?。
考虑一个简单的不区分大小写的键类型,可用于例如std::set
或std::map
:
// Represents case insensitive keys
struct CiKey : std::string {
using std::string::string;
using std::string::operator=;
bool operator<(CiKey const& other) const {
return boost::ilexicographical_compare(*this, other);
}
};
简单测试:
using KeySet = std::set<CiKey>;
using Mapping = std::pair<CiKey, int>; // Same with std::tuple
using Mappings = std::set<Mapping>;
int main()
{
KeySet keys { "one", "two", "ONE", "three" };
Mappings mappings {
{ "one", 1 }, { "two", 2 }, { "ONE", 1 }, { "three", 3 }
};
assert(keys.size() == 3);
assert(mappings.size() == 3);
}
使用C++17版本,两个断言均通过 (编译器浏览器)。
切换到C++20版本后,第二个断言失败了 (编译器浏览器)
output.s: ./example.cpp:28: int main(): Assertion `mappings.size() == 3' failed.
显而易见的解决方法
一个显而易见的解决方法是在C++20模式下有条件地提供 operator<=>
: 编译器浏览器
#if defined(__cpp_lib_three_way_comparison)
std::weak_ordering operator<=>(CiKey const& other) const {
if (boost::ilexicographical_compare(*this, other)) {
return std::weak_ordering::less;
} else if (boost::ilexicographical_compare(other, *this)) {
return std::weak_ordering::less;
}
return std::weak_ordering::equivalent;
}
#endif
问题
我惊讶地发现另一个破坏性更改的案例 - 即C++20更改代码行为而没有诊断。
根据我的阅读std::tuple::operator<
,它应该可以工作:
3-6)按字典顺序比较
lhs
和rhs
,即比较第一个元素,如果它们相等,则比较第二个元素,如果那些相等,则比较第三个元素,以此类推。 对于非空元组,(3)等价于
if (std::get<0>(lhs) < std::get<0>(rhs)) return true; if (std::get<0>(rhs) < std::get<0>(lhs)) return false; if (std::get<1>(lhs) < std::get<1>(rhs)) return true; if (std::get<1>(rhs) < std::get<1>(lhs)) return false; ... return std::get<N - 1>(lhs) < std::get<N - 1>(rhs);
我知道从C++20开始这些技术上不再适用,取而代之的是:
通过合成三路比较(见下文),按字典顺序比较
lhs
和rhs
,即比较第一个元素,如果它们相等,则比较第二个元素,如果它们相等,则比较第三个元素,以此类推与此同时
<、<= 、>、>= 和!= 运算符分别从
operator<=>
和operator==
合成。(自C++20起)问题在于,
我的类型没有定义
operator<=>
或operator==
,正如这篇答案所指出的,提供额外的
operator<
也没问题,并且当评估简单表达式如a < b
时应该使用。
- C++20中的行为变化是否正确和有意义?
- 是否应该有诊断?
- 我们能使用其他工具发现类似的静默变化吗?感觉在代码库中扫描使用自定义类型的
tuple
/pair
不可行。- 是否有其他类型,除了
tuple
/pair
,可能表现出类似的变化?
std::string
定义了这些运算符,因此由于从派生到基类的转换,它成为了一个候选。我相信所有支持比较的标准库类型都已经进行了成员重构。 - StoryTeller - Unslander Monica