[英]c++20 default comparison operator and empty base class
c++20默認比較運算符是一個非常方便的功能。 但是我發現如果 class 有一個空的基礎 class,它的用處就會減少。
默認操作符<=>通過依次比較基數(從左到右深度優先)和非靜態成員(按聲明順序)T的子對象來計算<=>,遞歸擴展數組成員(在增加下標的順序),並在發現不相等的結果時提前停止
根據標准,如果base
沒有 operator<=>,則SComparable
不會有 operator<=>。 在我看來,為空類定義比較運算符是沒有意義的。 因此,默認比較運算符不適用於具有空基 class 的類。
struct base {};
struct SComparable: base {
int m_n;
auto operator<=>(SComparable const&) const& = default; // default deleted, clang gives a warning
};
struct SNotComparable: base {
int m_n;
};
如果我們不顧一切地使用默認比較運算符,因此為空base
class 定義比較運算符。 另一個派生的 class SNotComparable
錯誤地變得可比較,因為它的空基 class base
。
struct base {
auto operator<=>(base const&) const& = default;
};
struct SComparable: base {
int m_n;
auto operator<=>(SComparable const&) const& = default;
};
struct SNotComparable: base { // SNotComparable is wrongly comparable!
int m_n;
};
那么對於具有空基 class 的類使用默認比較運算符的推薦解決方案是什么?
編輯:一些答案建議在空基 class 中添加默認比較運算符,並在不可比較的派生類中顯式刪除比較運算符。
如果我們將默認比較運算符添加到一個非常常用的空基 class,突然它所有不可比較的派生類都是可比較的(總是返回 std::strong_ordering::equal)。 我們必須找到所有這些派生的不可比較類並顯式刪除它們的比較運算符。 如果我們錯過了一些 class 並且稍后想要使其具有可比性但忘記自定義其比較運算符(我們都會犯錯誤),我們會得到錯誤的結果,而不是像以前那樣在空基中沒有默認比較運算符而導致編譯錯誤。 那為什么我首先使用默認比較運算符? 我想節省一些努力而不是引入更多。
struct base {
auto operator<=>(base const&) const& = default;
};
struct SComparable: base {
int m_n;
auto operator<=>(SComparable const&) const& = default;
};
struct SNotComparable1: base {
int m_n;
auto operator<=>(SNotComparable1 const&) const& = delete;
};
struct SNotComparableN: base {
int m_n;
// oops, forget to delete the comparison operator!
// if later we want to make this class comparable but forget to customize comparison operator, we get a wrong result instead of a non-comparable compile error.
};
在我看來,為空類定義比較運算符是沒有意義的。
好吧,這顯然不是毫無意義的。 如果您想要做的是默認類型的比較,那必然意味着比較您類型的所有子對象,包括基本的 class 子對象,這要求它們具有可比性——即使它們是空的。
你需要做的是提供它們——只是有條件的。 這樣做的最簡單方法可能是提供不同的空基 class:
struct base { /* ... */ };
struct comparable_base : base {
friend constexpr auto operator==(comparable_base, comparable_base)
-> bool
{
return true;
}
friend constexpr auto operator<=>(comparable_base, comparable_base)
-> std::strong_ordering
{
return std::strong_ordering::equal;
}
};
然后在您想要進行comparable_base
時從 compare_base 繼承,在您不進行比較時從base
繼承。 那是:
struct SComparable: comparable_base {
int m_n;
auto operator<=>(SComparable const&) const& = default;
};
struct SNotComparable: base {
int m_n;
};
我在那里使用隱藏的朋友比較只是為了能夠按值獲取類型 - 因為它是空的。 也可以輕松成為 function 的成員。
對於具有空基 class 的類使用默認比較運算符的推薦解決方案是什么?
解決方案是將默認比較器添加到基礎 class 中,然后如果您希望添加的SComparable
成員包含在比較中,則執行您在SComparable
中所做的操作 - 就像帶有成員的基礎 class 一樣。
如果您不希望將它們包含在比較中,請不要添加默認比較器,就像您在SNotComparable
中所做的那樣 - 並且將再次使用基本 class 比較器 - 就像在帶有成員的基本 class 中一樣。
如果您不希望 SNotComparable 中的基本SNotComparable
行為並且不希望SNotComparable
具有可比性,則delete
比較器,就像基本 class 有成員時一樣:
auto operator<=>(SNotComparable const&) const& = delete;
我想根據@Barry 的回答做一個小的修改。 我們可以有一個通用的混合 class compatible comparable<EmptyBase>
為任何空基提供可比較的運算符。 如果我們想對從空基類派生的 class 使用默認比較運算符,我們可以簡單地從comparable<base>
而不是base
派生這樣的 class 。 它也適用於鏈式空基comparable<base1<base2>>
。
struct base { /* ... */ };
template<typename EmptyBase>
struct comparable: EmptyBase {
static_assert(std::is_empty<EmptyBase>::value);
friend constexpr auto operator==(comparable, comparable)
-> bool
{
return true;
}
friend constexpr auto operator<=>(comparable, comparable)
-> std::strong_ordering
{
return std::strong_ordering::equal;
}
};
struct SComparableDefault: comparable<base> {
int m_n;
auto operator<=>(SComparableDefault const&) const& = default;
};
struct SNotComparable: base {
int m_n;
};
struct SComparableNotDefault: base {
int m_n;
constexpr bool operator==(SComparableNotDefault const& rhs) const& {
/* user defined... */
}
constexpr auto operator<=>(SComparableNotDefault const& rhs) const& {
/* user defined... */
}
};
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.