[英]Generating the necessary ref-qualified overloads for a member function
我有這個類:
template<typename T, size_t N>
class Array {
private:
T array[N];
public:
template <typename... InitValues>
constexpr Array(InitValues... init_values)
: array{ init_values... } {}
[[nodiscard]]
consteval int len() const noexcept { return sizeof(array) / sizeof(T); }
}
我想知道,對於這樣一個簡單的成員函數,我應該何時提供必要的ref-qualified
重載。
使用實際代碼,我可以編譯並運行以下代碼:
constexpr collections::Array a = collections::Array<long, 5>{1L, 2L, 3L};
SECTION("length of the array") {
REQUIRE( a.len() == 5 );
REQUIRE( collections::Array<int, 1>{1}.len() == 1 );
}
1- 為什么我可以編譯包含rvalue
調用的第二個REQUIRE
?
現在我要把len()
成員函數改成這樣:
[[nodiscard]]
consteval int len() const& noexcept { return sizeof(array) / sizeof(T); }
2-為什么我可以用const&
編譯兩者? 我想它們是兩種不同的 ref-qualified 用法。 我假設我可以用第一個調用,它是一個lvalue
,但不明白為什么我可以編譯第二個將len()
方法定義為const&
。
最后一次變更:
[[nodiscard]]
consteval int len() const&& noexcept { return sizeof(array) / sizeof(T); }
最后,我在a.get<I>()
上遇到編譯器錯誤。
'this' argument to member function 'len' is an lvalue, but function has rvalue ref-qualifier
REQUIRE( a.len() == 5 );
如果我評論那行代碼並且我只是運行,那就完美了:
REQUIRE( collections::Array<int, 1>{1}.len() == 1 );
而且我還可以使用std::move(a)
將a
轉換為rvalue reference
並編譯代碼。 但我不想那樣做。
ref-qualified
重載,對這些示例進行編碼的正確方法是什么?編輯:
我將添加另一個成員函數,它可能會根據 ref-qualified 實現(或者我認為可能發生的事情)做不同的事情:
template <size_t I>
requires concepts::AccessInBounds<I, N>
constexpr T get() const noexcept {
return array[I];
}
template <size_t I>
requires concepts::AccessInBounds<I, N>
constexpr T& get() const& noexcept {
return array[I];
}
問題1:為什么不呢? 規則與左值相同:無論對象的常量性如何,都可以調用 const 成員函數。
對於問題 2:因為它意味着與具有 const& 函數參數相同:可以使用任何左值或右值調用該函數。 它的存在主要是為了讓您區分左值和右值重載:
class Array {
// These two declarations would be ambiguous for Array rvalues
// int len() const;
// int len() &&;
// These are not: your test expressions will use different overloads
int len() const&;
int len() &&;
};
您編輯中的兩個函數對於左值和右值也是不明確的。 一個激勵性的例子會更多地遵循這些思路:假設我的類為某些資源提供功能,這些資源復制起來可能很昂貴,但移動起來更便宜,比如 std::vector。
template<class T>
class VectorView {
std::vector<T> vector;
public:
// ...
constexpr std::vector<T> const& base() const noexcept { return vector; }
};
現在,此類的用戶無法將矢量數據的所有權從視圖對象傳回,即使這在對右值調用 base() 函數時很有用。 因為避免為您不需要的東西付費符合 C++ 的精神,您可以通過添加右值限定的重載來實現這一點,該重載代替使用 std::move 返回右值引用。
因此,是否需要這種重載的答案取決於它,不幸的是,這也符合 C++ 的精神。 如果您正在為標准庫實現類似於我的示例類的東西,那么您當然會這樣做,因為它基於std::ranges::owning_view 。 正如您在該頁面上看到的那樣,它涵蓋了所有四種可能的 base()。 如果您只是使用對源范圍的引用,那么從該對象移動將是意外且不合適的,因此相關的ref_view只有一個 const base() 函數,就像我寫的那樣。
編輯至於移動語義,數組和向量之間的區別在於Array<T,N>
基於T[N]
,而std::vector<T>
基於T*
。 移動數組需要N次移動操作(線性時間復雜度),移動是否是對副本的改進取決於T。另外,它需要2N個元素的內存空間。 另一方面,一個向量只需要三個指針來完成它的工作,所以它可以在常數時間內移動,而復制仍然需要線性時間。
簡而言之,這種潛在的收益是移動語義和右值引用的基本原理。 還具有 && 限定成員函數的能力完善了此語言功能,但不如移動構造函數和賦值函數重要。 我還發現這個問題的答案很有用,因為它們提供了更多引用限定重載的示例。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.