不能对 std::set<std::string, std::less 使用用户提供的比较函数<>>
Cannot use user-provided comparison function for std::set<std::string, std::less<>>
在std::less
的帮助下,我偶然发现了使用透明比较器的std::set
的非常奇怪的编译错误。考虑一下这个简单的程序:
using Key = std::string;
bool operator<(const Key&, int) { return true; }
bool operator<(int, const Key&) { return true; }
int main()
{
std::set<Key, std::less<>> s;
int x;
auto it = s.find(x);
}
它给了我编译错误:
error: no matching function for call to object of type 'const std::less<void>'
if (__j != end() && _M_impl._M_key_compare(__k, _S_key(__j._M_node)))
^~~~~~~~~~~~~~~~~~~~~~
如果我使用自己的类而不是std::string
作为密钥,它会很好地工作:
struct My {};
bool operator<(const My&, const My&) { return true; }
using Key = My;
为什么它不适用于std::string
?
请在此处查看演示:https://gcc.godbolt.org/z/MY-Y2s
UPD
我真正想做的是声明std::unique_ptr<T>
和T*
之间的比较运算符。但我认为std::string
会更清楚。
依赖于参数的查找是一件有趣的老事情,不是吗?
在名称空间std
中已经存在与std::string
相关的operator<
,这是在寻找适合您的参数的<
时发现的。也许与直觉相反(但并非没有充分的理由),之后不会尝试进一步查找!不搜索其他命名空间。即使只有您的重载才真正匹配这两个参数。您的全局operator<
在这种情况下被有效地隐藏起来,如以下可怕的示例所示:
namespace N
{
struct Foo {};
bool operator<(Foo, Foo) { return false; }
}
bool operator<(N::Foo, int) { return false; }
namespace N
{
template <typename T1, typename T2>
bool less(T1 lhs, T2 rhs)
{
return lhs < rhs;
}
}
int main()
{
N::Foo f;
N::less(f, 3);
}
/*
main.cpp: In instantiation of 'bool N::less(T1, T2) [with T1 = N::Foo; T2 = int]':
main.cpp:22:17: required from here
main.cpp:15:20: error: no match for 'operator<' (operand types are 'N::Foo' and 'int')
return lhs < rhs;
~~~~^~~~~
*/
(现场演示)
现在,您不能向命名空间std
添加内容,但这很好,因为如果您无论如何都不重载与其他人的类型相关的运算符,它会更好。当其他库做同样的事情时,这是隐藏ODR错误的最快方法。
类似地,创建一个名为Key
的东西,实际上是,只是伪装成std::string
,这会导致意外冲突和意外行为。
总的来说,我强烈建议您的Key
至少是std::string
的"强别名";也就是说,它自己的类型而不是简单的别名。正如您所发现的,这也解决了您的问题,因为现在operator<
和操作数类型在同一个命名空间中。
更一般地说,如果您不是真的在这里使用别名,但确实想对标准类型进行操作,那么您将重新编写一个命名的自定义比较器,它可以很好地隔离新逻辑,而且使用起来也很简单。当然,不利的一面是你每次都必须"选择加入",但我认为这总体上是值得的。
- 使用std::multimap迭代器创建std::list
- C++中std::resize(n)和std::shrink_to_fit之间的区别
- 来自 std::list 的迭代器 .end() 按预期返回"0xcdcdcdcdcdcdcdcd"但 .begin()
- 错误:调用"std::vector<:vector<int>>::p ush_back(std::vector<std::__cxx11::basic_string<
- C 建造者Clang STD :: Sill,找不到超载的操作员&lt;
- 为什么STD :: MAP需要操作员&lt;以及我如何写一个
- std::vector::reserve(未知m),我知道m<<;N(通常)并且知道N
- std::vector<;uint8_t>;当C++11/14启用时,手动复制而不是调用memcpy
- C++运算符<<调用::ostream而不是std::osttream
- 是std :: set&lt; std :: future&gt;不可能存在
- 在修改列表后,std :: list&lt; t&gt; :: end()的值是否会更改
- BOOST ::变体无法解决运算符&lt;&lt;对于STD :: Ostream
- C++重载<<具有typedef'd std::vector
- 以x的倍数填充前导零,使用std::cout<<std::十六进制
- 错误:没有匹配'运算符<<"在'std::cout
- std::pair的默认构造函数<>将基本类型(int等)设置为零
- std::ostream&operator< & lt; (std:: ostream&压力,压力& &;val)
- 将std::endl传递给std::operator<<
- std::映射<>或std::vector<>在处理大型标志集时
- 重载& lt; & lt;使用命名空间std