我正在编写模板函数,模板参数为X
。在该函数内部,我想创建std::set<std::pair<int, X>>
,以便:
int
)字段排序(我不关心如何破坏关系).first
的对象,只要他们的.second
不相同如果我知道模板参数X
始终定义<
,那么最基本的std::set<std::pair<int, X>
(使用默认比较器)将完全正常。不幸的是,我不能假设X
。
我在考虑“欺骗”并使用基于指针的X
字段比较:
template <typename X>
struct DistCmp {
bool operator()(const std::pair<int, X>& lhs, const std::pair<int, X>& rhs) const {
return lhs.first < rhs.first || lhs.first == rhs.first && &lhs.second < &rhs.second;
}
};
template <typename X>
void f() {
std::set<std::pair<int, X>, DistCmp<X>> s{};
// ...
}
(毕竟,我并不关心如何 .second
进行比较,只要不对不相同的对象进行同等比较。)
不幸的是,我不认为这是正确的。部分是因为C ++标准here的引用(它表明指针比较,一般来说,未指定,因此我不能依赖它们对于不相同的对象不相等)。部分我只觉得它是可疑的/黑客的。
针对此问题是否有任何干净/便携的解决方案?
更新
我想到的一种方法是使用==
来比较指针而不是<
。但是,这并不好,因为它会导致pair<1, x> < pair<1, y> && pair<1, y> < pair<1, x>
为true
。这违反了strict weak ordering的要求,可能会导致stuff to break。
答案 0 :(得分:-1)
简短的回答:使用std :: multiset和sidestep整个问题 - multiset允许多个键,所以只需比较对的第一个元素。
注意,如果您想在地图中禁止使用相同的X值,则需要向X添加要求,因此它至少是EqualityComparable。否则,当X的值相同且不同时,您甚至无法检测到。
更长的回答: 您的代码不会产生您希望的结果。考虑添加新对&lt; 0,x&gt;使用&lt; 0,x&gt;进行映射。 std :: map将尝试查找&lt; 0,x&gt;的存在使用&lt; 0,x&gt;的临时副本。它将使用x的地址(临时!),它会将false与所有内容进行比较,即在map中,std :: map将根据x的地址(临时!)找到要插入的位置。然后它会复制x,从而改变地址并可能破坏它自己的顺序。
答案 1 :(得分:-1)
我认为::std::map< int, ::std::set< X > >
符合您的两个标准。
更新:由于可能没有<
运算符来比较X实例,您可能需要编写具有专业化的模板:
#include <map>
#include <set>
#include <vector>
#include <type_traits>
#include <algorithm>
#include <utility>
// Default implementation is used when there is no < operator to compare T instances,
// but there is == operator.
template< typename T, typename TDummy = void >
t_FancyContainerHelper final
{
public: using
t_Values = ::std::vector< T >;
public: using
t_IntToValues = ::std::map< int, t_Values >;
public: static void
Insert(t_IntToValues & int_to_values, int const key, T const & value)
{
auto p_pair{int_to_values.find(key)};
if(int_to_values.end() != p_pair)
(
auto const & values(p_pair->second);
if(values.end() != ::std::find(values.begin(), values.end(), value))
{
return;
}
}
else
{
p_pair = int_to_values.emplace
(
::std::piecewise_construct
, ::std::forward_as_tuple(key)
, ::std::forward_as_tuple()
).second;
}
auto & values(p_pair->second);
values.emplace(value);
}
};
// This specialization is used when there is < operator to compare T instances.
template< typename T > class
t_FancyContainerHelper
<
T
, ::std::enable_if_t
<
::std::is_same
<
bool
, decltype
(
::std::declval< T const & >()
<
::std::declval< T const & >()
)
>
>
> final
{
public: using
t_Values = ::std::set< T >;
public: using
t_IntToValues = ::std::map< int, t_Values >;
public: static void
Insert(t_IntToValues & int_to_values, int const key, T const & value)
{
auto p_pair{int_to_values.find(key)};
if(int_to_values.end() != p_pair)
(
auto const & values(p_pair->second);
if(values.end() != values.find(value))
{
return;
}
}
else
{
p_pair = int_to_values.emplace
(
::std::piecewise_construct
, ::std::forward_as_tuple(key)
, ::std::forward_as_tuple()
).second;
}
auto & values(p_pair->second);
values.emplace(value);
}
};