簡體   English   中英

operator==() 代碼編譯 w/C++17 但不編譯 C++20

[英]operator==() code compiles w/C++17 but not C++20

這個代碼片段

#include <stdlib.h>

struct Base { };
template<typename T>
inline bool operator==(const T&, const Base&)
{
    return true;
}
template<typename T>
inline bool operator!=(const T& lhs, const Base& rhs)
{
    return !(lhs == rhs);
}

struct A : public Base
{
    bool operator==(const A&) const { return true; }
};

struct A_1 final : public A { };

int main()
{
    const A a;
    const A_1 a_1;
    if (a_1 != a) {}

    return EXIT_SUCCESS;
}

在 C++17 (Visual Studio 2022) 中編譯沒有錯誤。 (有關更詳細的示例,請參閱C++17 operator==() 和 operator!=() code failed with C++20 ;注意在這種情況下代碼編譯。)

嘗試使用 C++20 構建相同的代碼會產生三個編譯器錯誤:

error C2666: 'operator !=': 3 overloads have similar conversions
message : could be 'bool A::operator ==(const A &) const' [rewritten expression '!(x == y)']
message : or 'bool A::operator ==(const A &) const' [synthesized expression '!(y == x)']
message : or       'bool operator !=<A_1>(const T &,const Base &)'

是的,我知道C++20 會合成operator!=之類的……但是現有的 C++17 代碼不應該仍然用 C++20 編譯嗎?

如何解決問題,以便相同的代碼同時使用 C++17 和 C++20 進行編譯生成相同的結果?

對派生類進行更改可能很困難,因為該代碼可能位於其他地方(這實際上是庫代碼); 這將是喜歡以更改Base

這是因為所有的功能現在都是候選的,而在它們之前不是。

這意味着突然之間, Aoperator==成為a_1 != a的候選者。 現在允許編譯器反轉參數,將a == b更改為!(a != b) ,反之亦然,甚至可以將順序更改為b == a

由於bool operator==(const A&);代碼導致不明確的調用bool operator==(const A&); in A支持a_1 != a ,通過將操作反轉為!(a_1 == a)並更改參數順序以最終獲得!(a == a_1)作為候選對象。

對此有多種解決方案。

一種是通過繼承函數簡單地使一個候選人變得更好:

struct A_1 final : public A { using A::operator==; };

另一種是限制操作員只與A一起工作:

struct A : public Base
{
    friend bool operator==(std::same_as<A> auto const&, std::same_as<A> auto const&) { return true; }
};

另一種不需要更改AA_1解決方案是添加一個重載,該重載始終是Base的理想候選對象作為友元函數。

整個代碼在 C++20 中變成了這樣:

struct Base {
    friend bool operator==(std::derived_from<Base> auto const&, std::derived_from<Base> auto const&) { return true; }
};

struct A : Base {};
struct A_1 final : A {};

您可以刪除全局命名空間中的模板函數,也可以刪除派生類中的函數。

您不需要刪除A中的函數,但它不會被調用並且生成的代碼仍然會非常令人驚訝。


但是,請注意您的代碼之前已損壞。

這是在 C++17 中使用operator==代碼的編譯器輸出:

int main()
{
    const A a;
    const A_1 a_1;
    if (!(a_1 == a)) {}

    return EXIT_SUCCESS;
}

編譯器輸出:

<source>: In function 'int main()':
<source>:26:15: error: ambiguous overload for 'operator==' (operand types are 'const A_1' and 'const A')
   26 |     if (!(a_1 == a)) {}
      |           ~~~ ^~ ~
      |           |      |
      |           |      const A
      |           const A_1
<source>:17:10: note: candidate: 'bool A::operator==(const A&) const'
   17 |     bool operator==(const A&) const { return true; }
      |          ^~~~~~~~
<source>:5:13: note: candidate: 'bool operator==(const T&, const Base&) [with T = A_1]'
    5 | inline bool operator==(const T&, const Base&)
      |             ^~~~~~~~
ASM generation compiler returned: 1

C++20 只會接受更少的具有令人驚訝的行為的代碼。

它的罰款,如果你刪除A::operator==

A::operator==真的沒有意義,因為模板說一切都等於Base

但是現有的 C++17 代碼不應該仍然用 C++20 編譯嗎?

委員會努力盡量減少破壞性更改,但他們不保證不會有任何更改。 在這種情況下,人們認為讓==成為等價關系比保持現有行為更重要。 正如鏈接的問題所指出的,多態相等性測試通常是錯誤的來源。

C++ 擴展了關系運算符的重載解析以包含交換的參數,這在診斷消息中:

  • GCC 將候選標記為“(反向)”和
  • Clang 提及(使用反向參數順序)

所以你需要更少的操作員。 事實上,您可以考慮重載/默認三向比較運算符 ( <=> )。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM