一、介绍
之前在模板相关的技术中对ADL(Argument Dependent Lookup,参数依赖查找)进行过分析和举例说明。但在实际的应用中,如果迭代器是危险的指针类型时,其不会进行ADL的动作。但如果其类似于模板projected<Danger*, identity>这种类型时,ADL的操作就会进行。但对于Ranges库来说,由于其缺乏ADL的防护性,在某些情况下就会导致异常。所以在C++26中重新规范了
projected<I, Proj>,从而修复了这个问题即其相关联实体不再包含其模板参数。
不过一般情况下,原生指针的大多数操作都不会触发ADL的行为。比如下面的情况:
Holder<Incomplete> *a[10] = {}; // ten null pointers
Holder<Incomplete> **p = a; // OK
p += 1; // OK
assert(*p == nullptr); // OK
assert(p == a+1); // OK
assert(std::count(a, a+10, nullptr) == 10); // OK
二、C++20中的问题和解决方式
看一下C++20中的Ranges中的例子:
template<class T> struct Holder { T t; };
struct Incomplete;
Holder<Incomplete> *a[10] = {}; // 十个空指针
assert(std::count(a, a+10, nullptr) == 10); // 正常
assert(std::ranges::count(a, a+10, nullptr) == 10); // 硬错误
这段代码出错误的原因就在于indirectly_comparable<T*, T*, Pred>中利用到了projected<T*, identity>的所有关联类型,而T中包含Holder。
针对上面的问题,在C++26中提出了下面的解决方式就是引入ADL防护即不再让T成为相关的关联类型。也就是ADL-proof std::projected。
template<class Associated>
struct projected { };
//将上面的projected替换为下面的:
template<class T>
struct __projected_impl {
struct type { };
};
template<class NonAssociated>
using projected = __projected_impl<NonAssociated>::type;
ADL会关联派生类的基类,但不会关联嵌套类的包含类即__projected_impl::type中的::充当了防止不需要的 ADL的防火墙。
三、另外一种技术实现
另外,在一些提案中有“phrase of power”这种技术,如果这种技术能够应用,也可能使用这种技术来替代相关的projected的实现。其实就是在类模板的模板头增加一些特定的标记,arguments are not associated entities即参数不是关联实现。假如可以使用这种技术,则相关的应用就可以使用下面的方式:
using T = Holder<Incomplete>*;
static_assert(std::equality_comparable<T>); // 正常
static_assert(std::indirectly_comparable<T*, T*, std::equal_to<>>); // 将会正常
static_assert(std::sortable<T*>); // 将会正常
int main() {
Holder<Incomplete> *a[10] = {}; // 十个空指针
assert(std::count(a, a+10, nullptr) == 10); // 正常
assert(std::ranges::count(a, a+10, nullptr) == 10); // 将会正常
}
四、总结
从这些细节来看,一如以前的标准,C++26中也对原来遗留下来的技术债在不断的偿还。既有功能的完善和新的技术的解决方式,这些都是保障C++不断向前发展的一个重要推动力量。我们的学习,应该也和标准的演进保持一致。查漏补缺,不断向上。

61





