簡體   English   中英

為成員函數生成必要的引用限定重載

[英]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.

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