我想为pod结构体X
创建一个std::set<X>
。我试着提供一个免费的operator<
。我不希望这个运算符具有全局效应(在编译单元之外),所以我将operator<
设置为static。(链接到代码)
struct X { int value; };
static bool operator< (const X& a, const X& b) { return a.value < b.value; }
void foo() {
std::set<X> some_set;
some_set.insert({1});
}
这个编译得很好。
但是,如果我将operator<
移动到未命名的命名空间中(链接到代码)
struct X { int value; };
namespace {
bool operator< (const X& a, const X& b) { return a.value < b.value; }
}
void foo() {
std::set<X> some_set;
some_set.insert({1});
}
应该是等价的
然后std::less<X>
抱怨:
usr/local/include/c++/12.1.0/bits/stl_function.h:408:20: error: no match for 'operator<' (operand types are 'const X' and 'const X')
{ return __x < __y; }
好的,我可以用一个自定义比较器来代替。
但是有谁能解释为什么上面的不起作用吗?一般来说,是否有可能在同一结构的编译单元中提供不同的操作符实现?
这可以归结为:
#include <iostream>
struct A {};
namespace
{
void foo(A) {} // Your `operator<`.
}
namespace N
{
void foo(int) {} // Some random `operator<` in `std`.
template <typename T>
void bar(T value)
{
foo(value);
}
}
int main()
{
N::bar(A{});
}
这不起作用,因为N::foo
在未命名的命名空间中遮蔽了foo(A)
。
如果没有未命名的命名空间,foo(A)
将通过ADL找到,因为它将与A
在相同的命名空间中。
还要注意,这似乎违反了单定义规则。虽然您的operator<
是static
,但std::less
不是,并且它在不同翻译单元中的实例化将选择不同的operator<
s,这似乎是非法的。
当来自不同TU的std::less
的内联实现最终被链接器合并时,这可能导致我们的operator<
中的一个在每个TU中使用。