簡體   English   中英

動態選擇要在 std::map 中使用的比較函子

[英]Dynamically selecting the comparison functor to be used in std::map

我有一組數據需要按照用戶在運行時決定的標准進行排序。

理想情況下,這個排序標准必須作為 function 中的參數傳遞,例如:

void mainFunction(
    InputData const &inputData,
    OutputData &outputData,
    BaseSortingCriteria const &compareFunctor)
{

    /* The data is sorted by this map using the custom provided functor as criteria */
    std::map<InputDataType, ValueType, BaseSortingCriteria> sortedSets(compareFunctor);

    ...
}

為此,我創建了一個表示基本標准的虛擬函子,例如:

struct VirtualSortingCriteria
{
    virtual bool operator()(
        const InputDataType &var1,
        const InputDataType &var2) const = 0;
}

為了保持一個通用接口,一個基本函子,它簡單地執行一個在構造過程中傳遞的“真實”函子:

struct BaseSortingCriteria
{
    BaseSortingCriteria(
        std::shared_ptr<const VirtualSortingCriteria> pCompareFunctor) :
        m_realCompareFunctor(pCompareFunctor)
    {
    }

    bool operator()(
        const InputDataType &var1,
        const InputDataType &var2)
    {
        return m_realCompareFunctor->operator()(var1, var2);
    }

private:
    /** Pointer to the real functor to be used. */
    std::shared_ptr<const VirtualSortingCriteria> const m_realCompareFunctor;
}

我已經定義了幾個“真正的”函子來測試:

struct RealFunctorVersionA final : public VirtualSortingCriteria
{
    bool operator () (
        InputDataType const &var1,
        InputDataType const &var2) const;
};

struct RealFunctorVersionB final : public VirtualSortingCriteria
{
    bool operator () (
        InputDataType const &var1,
        InputDataType const &var2) const;
};

實際使用這些不同排序標准的代碼如下所示:

std::shared_ptr<VirtualSortingCriteria> cmpFunctor;
switch(userSelectedSortingCriteria)
{
    case CRITERIA_A:
        cmpFunctor.reset(new RealFunctorVersionA);
        break;
    case CRITERIA_B:
        cmpFunctor.reset(new RealFunctorVersionB);
        break;
    default:
        break;
}

BaseSortingCriteria baseCmpFunctor(cmpFunctor);
mainFunction(inputData, outputData, baseCmpFunctor);

所有這一切都很好。 但是,我認為實現我想要的東西太復雜了,而且我感覺必須使用多態特性意味着“真正的”函子不能再被內聯,導致可能(雖然我還沒有測量) 性能損失。

我不確定這是否可以使用模板以更簡單的方式修復(因為函子選擇是在運行時完成的)。

有什么建議么? 還是我只是想多了這個問題,這是一個可以接受的解決方案? 性能呢?

我不在mainFunction()接口中使用純 C 風格的函數(bool (*)(InputDataType const &var1, InputDataType const &var2))來訪問仿函數提供的額外功能,例如 state、構造參數、等等

非常感謝您的建議。

您可以使用std::function代替多態性和滾動您自己的類型擦除。 例如:

struct RealFunctorVersionA
{
    bool operator () (const InputDataType& var1, const InputDataType& var2) const;
};

struct RealFunctorVersionB
{
    bool operator () (const InputDataType& var1, const InputDataType& var2) const;
};

using MyMapType = std::map<
    InputDataType,
    ValueType,
    const std::function<bool(const InputDataType&, const InputDataType&)>
>;

MyMapType map_a{RealFunctorVersionA{}}
MyMapType map_b{RealFunctorVersionB{}}

現場演示

現在你不需要你的基礎 class。 取而代之的是std::function負責類型擦除和存儲(副本)用於構造地圖比較器的函數 object。

另請注意,我已標記比較器const以便在構建 map 后無法更改它。 這樣做很容易導致未定義的行為。


至於性能,如果您使用運行時調度方法,您的 function 調用可以並且永遠不會被內聯。 Function 調用內聯是一個編譯時進程,因此當您在編譯時有一組潛在的無限潛在調用時,它根本無法完成。 不過,請始終警惕過早的優化。 永遠不要在沒有基准測試的情況下做出性能決策。

話雖如此,但在性能方面, std::function可能與您現在所擁有的非常相似。 std::function和虛擬調度通過非常相似的機制工作。


如果您希望編譯器可能內聯對比較器的調用,則需要使其靜態可調用。 您可以使用一些 state 來使調用具有不同的行為。 例如:

struct MyComparator
{
    bool reverse;
    bool operator () (const InputDataType& var1, const InputDataType& var2) const
    {
        // ...
        if (reverse) {
            return var1 > var2;
        } else {
            return var1 < var2;
        }
    }
};

using MyMapType = std::map<
    InputDataType,
    ValueType,
    const MyComparator
>;

MyMapType map_a{MyComparator{false}};
MyMapType map_a{MyComparator{true}};

現場演示

這顯然不太靈活,但它可以提供更好的性能。 同樣,您必須進行基准測試才能確定增益是否可測量,更不可感知。

暫無
暫無

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

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