假设我具有以下Data
类:
struct Data {
char foo[8];
char bar;
};
和以下函数my_algorithm
,该函数需要一对char *
(类似于STL算法):
void my_algorithm(char *first, char *last);
对于Data
的{{1}}数据成员,而不是像这样调用foo
:
my_algorithm()
我可以使用std::begin()
和std::end()
便捷功能模板:
Data data;
my_algorithm(data.foo, data.foo + 8);
我想实现类似于my_algorithm(std::begin(data.foo), std::end(data.foo));
的{{1}}数据成员的功能。也就是说,不用写:
Data
我想写些类似的东西:
bar
因此,在这种情况下,我定义了以下两个普通(非模板)函数:
my_algorithm(&data.bar, &data.bar + 1);
这样我就可以编写如下代码:
my_algorithm(begin(data.bar), end(data.bar));
使用上面的char* begin(char& c) { return &c; }
char* end(char& c) { return &c + 1; }
声明,我希望Data data;
using std::begin;
using std::end;
my_algorithm(begin(data.foo), end(data.foo)); // ok - std::begin()/std::end()
my_algorithm(begin(data.bar), end(data.bar)); // Error!!!
/ using
和std::begin()
/ std::end()
分别处于相同的重载集中。由于函数::begin()
和::end()
是后一个调用的完美匹配,并且它们不是模板,所以我希望对::begin()
的最后一个调用能够与它们匹配。但是,根本不考虑普通功能。结果,编译失败,因为::end()
和my_algorithm()
与呼叫不匹配。
基本上,后面的调用就像我写的一样:
std::begin()
也就是说,重载解析过程仅考虑功能模板(即std::end()
/ my_algorithm(begin<>(data.bar), end<>(data.bar));
),而不考虑普通功能(即不考虑std::begin()
/ {{1 }}。
如果我完全符合std::end()
/ ::begin()
的通话条件,则只能按预期工作:
::end()
我在这里想念什么?
答案 0 :(得分:6)
让我们得到一个完整的,可复制的示例:
#include <iterator>
char* begin(char& c) { return &c; }
char* end(char& c) { return &c + 1; }
namespace ns {
void my_algorithm(char *first, char *last);
void my_function() {
using std::begin;
using std::end;
char c = '0';
my_algorithm(begin(c), end(c));
}
}
当您对begin(c)
和end(c)
进行不合格的调用时,编译器将经历unqualified name lookup的过程(在Argument-dependent lookup page of cppreference上进行了介绍)。
对于常规的非限定名称查找,该过程大致从当前所在的名称空间开始-在这种情况下为::ns
-仅在找不到特定名称的情况下才移出名称空间。
如果函数调用不合格(如begin(c)
和end(c)
一样,则可能会发生argument dependent lookup,这会发现在与函数类型相同的名称空间中声明的自由函数'参数,通过查找“关联名称空间”扩展超载集合的过程。
但是,在这种情况下,char
是基本类型,因此依赖于参数的查找不允许我们查找全局::begin
和::end
函数。
对于基本类型的参数,关联的名称空间和类的集合为空
cppreference: argument dependent lookup
相反,正如我们已经拥有的using std::begin; using std::end;
一样,编译器已经可以看到begin(...)
和end(...)
的可能功能,即在命名空间::std
中定义的功能,而无需移出从::ns
到::
的名称空间。因此,编译器使用这些函数,并且编译失败。
值得注意的是,即使将using std::begin; using std::end;
放在::begin
中,::end
也会阻止编译器查找自定义::ns
和begin
。
您可以改写自己的end
和#include <iterator>
namespace ns {
char* begin(char& c) { return &c; }
char* end(char& c) { return &c + 1; }
template <typename T>
auto begin(T&& t) {
using std::begin;
// Not unbounded recursion if there's no `std::begin(t)`
// or ADL `begin(t)`, for the same reason that our
// char* begin(char& c); overload isn't found with
// using std::begin; begin(c);
return begin(t);
}
template <typename T>
auto end(T&& t) {
using std::end;
return end(t);
}
void my_algorithm(char *first, char *last);
void my_function() {
char c = '0';
my_algorithm(ns::begin(c), ns::end(c));
}
}
:
let baseUrl: String = "http://maps.apple.com/?q="
let encodedName = theAddress.addingPercentEncoding( withAllowedCharacters: .urlQueryAllowed) ?? ""
let finalUrl = baseUrl + encodedName
if let url = NSURL(string: finalUrl) {
UIApplication.shared.open(url)
}
答案 1 :(得分:-2)
问题的标题是“ 重载标准 :: begin()”。只有在相同范围内才可以重载。那就是您不能重载来自不同作用域的名称。在另一个范围内,我们只能努力帮助查找名称。本质上,这里的“使用std :: begin”声明隐藏 ::开始于问题代码。请参阅S.Lippman以获取参考:
属于两个不同命名空间的成员的函数不会彼此重载。
使用声明的范围。 using声明中引入的名称遵循正常的作用域规则。 隐藏在外部作用域中的具有相同名称的实体。
参数为char且char为基本类型时-不应考虑参数依赖的查找-如注释中所述-不存在与基本类型相关联的名称空间。 同样,问题是:“我想念什么?” -因此答案只针对原因-建议可能过于广泛。