简体   繁体   English

动态选择要在 std::map 中使用的比较函子

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

I have a set of data which needs to be sorted by a criteria that is decided by the user in runtime.我有一组数据需要按照用户在运行时决定的标准进行排序。

Ideally this sorting criteria has to be passed as a parameter in a function, such as:理想情况下,这个排序标准必须作为 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);

    ...
}

To get this, I've created a virtual functor representing the base criteria, such as:为此,我创建了一个表示基本标准的虚拟函子,例如:

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

And to keep a common interface, a base functor, which simply executes a "real" functor passed during construction:为了保持一个通用接口,一个基本函子,它简单地执行一个在构造过程中传递的“真实”函子:

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;
}

And I've defined a couple of "real" functors to test:我已经定义了几个“真正的”函子来测试:

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;
};

The code that actually uses these different sorting criterias looks like this:实际使用这些不同排序标准的代码如下所示:

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);

All of this works fine.所有这一切都很好。 However, I think it's too complicated to achieve what I want, plus I have the feeling that having to use the polymorphism feature implies the "real" functors can't be inlined anymore, resulting in a probably (although I haven't measured yet) performance penalty.但是,我认为实现我想要的东西太复杂了,而且我感觉必须使用多态特性意味着“真正的”函子不能再被内联,导致可能(虽然我还没有测量) 性能损失。

I'm not sure this could be fixed in a simpler manner using templates (since the functor selection is done at runtime).我不确定这是否可以使用模板以更简单的方式修复(因为函子选择是在运行时完成的)。

Any suggestions?有什么建议么? Or am I just overthinking the issue and this is an acceptable solution?还是我只是想多了这个问题,这是一个可以接受的解决方案? What about performance?性能呢?

I don't use plain C-style functions (bool (*)(InputDataType const &var1, InputDataType const &var2)) in the mainFunction() interface to be able to access the extra functionality provided by functors, such as state, construction parameters, etc.我不在mainFunction()接口中使用纯 C 风格的函数(bool (*)(InputDataType const &var1, InputDataType const &var2))来访问仿函数提供的额外功能,例如 state、构造参数、等等

Thanks a lot in advance for your advice.非常感谢您的建议。

Instead of polymorphism and rolling your own type erasure, you could use std::function .您可以使用std::function代替多态性和滚动您自己的类型擦除。 For example:例如:

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{}}

Live Demo现场演示

Now you don't need your base class.现在你不需要你的基础 class。 Instead std::function takes care of type-erasing and storing (a copy of) the fuctor object you use to construct your map's comparator.取而代之的是std::function负责类型擦除和存储(副本)用于构造地图比较器的函数 object。

Also note that I've marked the comparator const so that it can't be changed after the map is constructed.另请注意,我已标记比较器const以便在构建 map 后无法更改它。 Doing so could easily lead to undefined behavior.这样做很容易导致未定义的行为。


As for performance, your function calls can and will never be inlied if you use a runtime dispatch method.至于性能,如果您使用运行时调度方法,您的 function 调用可以并且永远不会被内联。 Function call inlining is a compile-time process, so it simply can't be done when you have a potentially unbounded set of potential calls at compile time. Function 调用内联是一个编译时进程,因此当您在编译时有一组潜在的无限潜在调用时,它根本无法完成。 Always be wary of premature optimization though.不过,请始终警惕过早的优化。 Never make performance decisions without benchmarking.永远不要在没有基准测试的情况下做出性能决策。

That being said, std::function will likely be very similar, performance wise, to what you have now though.话虽如此,但在性能方面, std::function可能与您现在所拥有的非常相似。 std::function and virtual dispatch work via very similar mechanisms. std::function和虚拟调度通过非常相似的机制工作。


If you want the compiler to potentially inline calls to your comparator, you'll need to make it statically callable.如果您希望编译器可能内联对比较器的调用,则需要使其静态可调用。 You could use some state to make the calls have different behavior though.您可以使用一些 state 来使调用具有不同的行为。 For example:例如:

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}};

Live Demo现场演示

This is obviously less flexible, but it could give better performance.这显然不太灵活,但它可以提供更好的性能。 Again, you would have to benchmark to say for sure if the gain is even measurable, much less perceptible.同样,您必须进行基准测试才能确定增益是否可测量,更不可感知。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM