我怎样才能防止在 ADL 期间被遮蔽?
How can I prevent overshadowing during ADL?
假设我有一个 class 扩展一个 (STL) 容器并提供一个习惯的 begin
成员函数:
#include <vector>
template <typename Cont>
struct Bar {
Cont c;
auto my_begin() { return begin(c); }
};
int main() {
Bar<std::vector<int>> b;
b.my_begin();
}
通过 ADL,我不必在 begin()
调用之前指定 std::
。这很好,因为 std::begin(v)
将始终尝试调用 v.begin()
,用户可能希望使用没有 .begin()
接口的自定义容器,因此如果他定义自己的自由函数 begin(v)
, Bar
会用到。
但是,如果我也将 my_begin
重命名为 begin
,ADL 似乎将不再起作用,就好像它被掩盖了一样。编译器只会抱怨它找不到匹配的调用以在 class 范围内开始:
prog.cc: In instantiation of 'auto Bar<Cont>::begin() [with Cont = std::vector<int>]':
prog.cc:11:13: required from here
prog.cc:6:27: error: use of 'auto Bar<Cont>::begin() [with Cont = std::vector<int>]' before deduction of 'auto'
6 | auto begin() { return begin(c); }
| ^~~~~
prog.cc:6:32: error: no matching function for call to 'Bar<std::vector<int> >::begin(std::vector<int>&)'
6 | auto begin() { return begin(c); }
| ~~~~~^~~
prog.cc:6:10: note: candidate: 'auto Bar<Cont>::begin() [with Cont = std::vector<int>]'
6 | auto begin() { return begin(c); }
| ^~~~~
prog.cc:6:10: note: candidate expects 0 arguments, 1 provided
当然,通过写std::begin
,代码会再次运行,但这只是表明排除了ADL。除了重命名成员函数,我还能做什么?
一个解决方案是编写一个独立的辅助函数,专门在 begin
:
上使用 ADL
template <typename C>
decltype(auto) adl_begin(C && c)
{
using std::begin;
return begin(std::forward<C>(c));
}
然后从您的 class 中调用 adl_begin
。
存在 using std::begin;
部分,因此该函数甚至可以处理原始数组。
您可以只使用两步法:
auto begin() {
using std::begin; //1
return begin(c); //2
}
1
: 考虑std::begin
2
:如果 c
在其命名空间中有 begin()
,则调用它,否则默认为 std::begin()
您可以阅读更多相关信息here
假设我有一个 class 扩展一个 (STL) 容器并提供一个习惯的 begin
成员函数:
#include <vector>
template <typename Cont>
struct Bar {
Cont c;
auto my_begin() { return begin(c); }
};
int main() {
Bar<std::vector<int>> b;
b.my_begin();
}
通过 ADL,我不必在 begin()
调用之前指定 std::
。这很好,因为 std::begin(v)
将始终尝试调用 v.begin()
,用户可能希望使用没有 .begin()
接口的自定义容器,因此如果他定义自己的自由函数 begin(v)
, Bar
会用到。
但是,如果我也将 my_begin
重命名为 begin
,ADL 似乎将不再起作用,就好像它被掩盖了一样。编译器只会抱怨它找不到匹配的调用以在 class 范围内开始:
prog.cc: In instantiation of 'auto Bar<Cont>::begin() [with Cont = std::vector<int>]':
prog.cc:11:13: required from here
prog.cc:6:27: error: use of 'auto Bar<Cont>::begin() [with Cont = std::vector<int>]' before deduction of 'auto'
6 | auto begin() { return begin(c); }
| ^~~~~
prog.cc:6:32: error: no matching function for call to 'Bar<std::vector<int> >::begin(std::vector<int>&)'
6 | auto begin() { return begin(c); }
| ~~~~~^~~
prog.cc:6:10: note: candidate: 'auto Bar<Cont>::begin() [with Cont = std::vector<int>]'
6 | auto begin() { return begin(c); }
| ^~~~~
prog.cc:6:10: note: candidate expects 0 arguments, 1 provided
当然,通过写std::begin
,代码会再次运行,但这只是表明排除了ADL。除了重命名成员函数,我还能做什么?
一个解决方案是编写一个独立的辅助函数,专门在 begin
:
template <typename C>
decltype(auto) adl_begin(C && c)
{
using std::begin;
return begin(std::forward<C>(c));
}
然后从您的 class 中调用 adl_begin
。
存在 using std::begin;
部分,因此该函数甚至可以处理原始数组。
您可以只使用两步法:
auto begin() {
using std::begin; //1
return begin(c); //2
}
1
: 考虑std::begin
2
:如果 c
在其命名空间中有 begin()
,则调用它,否则默认为 std::begin()
您可以阅读更多相关信息here