为非数组重载std :: begin()和std :: end()

时间:2019-06-03 17:30:40

标签: c++ c++11 templates overloading using-declaration

假设我具有以下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!!! / usingstd::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()

我在这里想念什么?

2 个答案:

答案 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也会阻止编译器查找自定义::nsbegin


您可以改写自己的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为基本类型时-不应考虑参数依赖的查找-如注释中所述-不存在与基本类型相关联的名称空间。   同样,问题是:“我想念什么?” -因此答案只针对原因-建议可能过于广泛。