动态选择要在 std::map 中使用的比较函子
Dynamically selecting the comparison functor to be used in std::map
我有一组数据需要根据用户在运行时决定的标准进行排序。
理想情况下,此排序标准必须作为函数中的参数传递,例如:
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;
}
并保留一个通用接口,一个基本仿函数,它简单地执行构造期间传递的 "real" 仿函数:
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;
}
我定义了几个 "real" 仿函数来测试:
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);
一切正常。但是,我认为实现我想要的东西太复杂了,而且我觉得必须使用多态特性意味着 "real" 函子不能再内联,导致可能(虽然我没有尚未测量)性能损失。
我不确定是否可以使用模板以更简单的方式解决这个问题(因为函子选择是在运行时完成的)。
有什么建议吗?或者我只是想多了这个问题,这是一个可以接受的解决方案?性能怎么样?
我不在 mainFunction()
接口中使用普通的 C 风格函数 (bool (*)(InputDataType const &var1, InputDataType const &var2))
来访问函子提供的额外功能,例如状态、构造参数等
非常感谢您的建议。
您可以使用 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
负责类型擦除和存储(副本)您用来构建地图比较器的 fuctor 对象。
另请注意,我已将比较器标记为 const
,以便在构建地图后无法更改它。这样做很容易导致未定义的行为。
至于性能,如果您使用运行时分派方法,您的函数调用可能而且永远不会被内嵌。函数调用内联是一个编译时过程,因此当您在编译时有一组可能无限的潜在调用时,它根本无法完成。不过,请始终警惕过早的优化。切勿在没有基准测试的情况下做出性能决策。
也就是说,std::function
在性能方面可能与您现在拥有的非常相似。 std::function
和虚拟调度通过非常相似的机制工作。
如果您希望编译器潜在地内联调用您的比较器,则需要使其可静态调用。不过,您可以使用某种状态使调用具有不同的行为。例如:
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}};
这显然不太灵活,但可以提供更好的性能。同样,您必须进行基准测试才能确定增益是否可测量,更不易察觉。
我有一组数据需要根据用户在运行时决定的标准进行排序。
理想情况下,此排序标准必须作为函数中的参数传递,例如:
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;
}
并保留一个通用接口,一个基本仿函数,它简单地执行构造期间传递的 "real" 仿函数:
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;
}
我定义了几个 "real" 仿函数来测试:
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);
一切正常。但是,我认为实现我想要的东西太复杂了,而且我觉得必须使用多态特性意味着 "real" 函子不能再内联,导致可能(虽然我没有尚未测量)性能损失。
我不确定是否可以使用模板以更简单的方式解决这个问题(因为函子选择是在运行时完成的)。
有什么建议吗?或者我只是想多了这个问题,这是一个可以接受的解决方案?性能怎么样?
我不在 mainFunction()
接口中使用普通的 C 风格函数 (bool (*)(InputDataType const &var1, InputDataType const &var2))
来访问函子提供的额外功能,例如状态、构造参数等
非常感谢您的建议。
您可以使用 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
负责类型擦除和存储(副本)您用来构建地图比较器的 fuctor 对象。
另请注意,我已将比较器标记为 const
,以便在构建地图后无法更改它。这样做很容易导致未定义的行为。
至于性能,如果您使用运行时分派方法,您的函数调用可能而且永远不会被内嵌。函数调用内联是一个编译时过程,因此当您在编译时有一组可能无限的潜在调用时,它根本无法完成。不过,请始终警惕过早的优化。切勿在没有基准测试的情况下做出性能决策。
也就是说,std::function
在性能方面可能与您现在拥有的非常相似。 std::function
和虚拟调度通过非常相似的机制工作。
如果您希望编译器潜在地内联调用您的比较器,则需要使其可静态调用。不过,您可以使用某种状态使调用具有不同的行为。例如:
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}};
这显然不太灵活,但可以提供更好的性能。同样,您必须进行基准测试才能确定增益是否可测量,更不易察觉。