在命名空间中使用自己的类比较 std::vector 不会编译
Comparing std::vector using own class in namespace does not compile
以下代码无法编译,因为找不到比较运算符。
#include <vector>
#include <iostream>
#include <string>
namespace Cool {
struct Person {
std::string name;
};
}
bool operator==(const Cool::Person& p1, const Cool::Person& p2) {
return p1.name == p2.name;
}
int main(int, char *[])
{
std::vector<Cool::Person> a{ {"test"} };
std::vector<Cool::Person> b{ {"test"} };
bool ok = a == b;
std::cout << ok << std::endl;
}
经过一些实验,我发现以下内容完美编译:
#include <vector>
#include <iostream>
#include <string>
namespace Cool {
struct Person {
std::string name;
};
bool operator==(const Person& p1, const Person& p2) {
return p1.name == p2.name;
}
}
int main(int, char *[])
{
std::vector<Cool::Person> a{ {"test"} };
std::vector<Cool::Person> b{ {"test"} };
bool ok = a == b;
std::cout << ok << std::endl;
}
有人可以解释这种行为背后的理由吗?
这称为 ADL 或与参数相关的查找。
对于运算符,编译器不仅会在当前命名空间中搜索合适的函数,还会在参数的命名空间中搜索。
例如:
int main() {
int arr[3] = {};
std::vector<int> vec(3);
auto b_vec = begin(vec); // std::begin
auto b_arr = begin(arr); // Error!
}
使用 vec 调用begin
时,它将在std
命名空间中搜索,因为std::vector
位于该命名空间中。对于原始数组,找不到该函数,因为它没有与该类型关联的命名空间。
关闭 ADL 的一种方法是简单地限定函数:
// calls the std:: one, not the boost:: one
std::begin(some_boost_container);
这也是好友函数的工作原理:
struct test {
friend void find_me(int) {}
};
find_me(3); // uh? no matching function?
即使该功能完全没问题,也找不到。
它需要在其参数中的类名称,因此 ADL 启动并在类范围中找到它:
struct test {
friend void find_me(test const&) {}
};
find_me(test{}); // works!
那么,对于运营商来说?为什么它有效?
因为调用用户定义的运算符大致等效于:
// arg1 == arg2;
operator==(arg1, arg2);
由于函数名称不是限定的,因此 ADL 将启动。然后在正确的命名空间中找到运算符,还可以找到友元函数。
这既允许一个很好的语法,也允许泛型函数调用命名空间内的函数。
那么为什么向量在全局命名空间中找不到那个呢?
这是因为 ADL 的工作原理。在包含名称的函数的命名空间内,ADL 将仅在参数的命名空间内搜索。但是,如果找不到,它将回退到正常查找。
namespace Cool {
struct Person {
std::string name;
};
}
bool cant_you_find_me(Cool::Person const& p);
namespace test {
void cant_you_find_me();
template<typename T>
void test_template(T const& p) {
cant_you_find_me(p); // ADL?
}
}
在此示例中,ADL 将搜索函数cant_you_find_me
,但如果无法通过 ADL 找到全局命名空间,则不会考虑全局命名空间,因为正常查找将找到最接近的命名空间:不带参数的命名空间。
这就是std
命名空间发生的情况。它在其中定义了许多operator==
。如果 ADL 找不到合适的命名空间,则不会考虑全局命名空间,而是考虑std
中的命名空间。
相关文章:
- 使用std::vector的OpenCL矩阵乘法
- POCO::PostgreSQL:如何将std::vector支持添加到`Binder::bind`
- std::vector的包装器,使数组的结构看起来像结构的数组
- 编译器如何区分std::vector的构造函数
- 使用 pqxx 将 std::vector 存储在 postgresql 中,并从数据库中检索它
- 在std::vector上存储带有模板的类实例
- 在main()之外初始化std::vector会导致性能下降(多线程)
- 为什么std::vector比数组慢
- std::vector::迭代器是否可以合法地作为指针
- 如何将二进制格式的 C++ 对象的 std::vector 保存到磁盘?
- 为什么std::vector和std::valarray初始化构造函数不同
- ";结果类型必须是可从输入范围的值类型""构造的;创建std::vector时
- 在没有未定义行为的情况下实现类似std::vector的容器
- 如何调整 std::vector of Eigen::MatrixXd 的大小
- 使用 std::vector::reverse_iterator 将 int 序列化为字节向量?
- 如何将AERT_Allocate与 std:vector 一起使用
- 推导 std::vector::back() 的返回类型
- 如何将原始字节附加到 std::vector?
- std::vector 没有重载函数的实例与参数列表匹配
- 如果 KEY 是 std::list 或 std::vector 而不是值,那么 std::map 的默认行为是什么?