前段时间我读了一篇文章解释了参数依赖查找的几个缺陷,但我再也找不到了.它是关于获取您不应该访问的东西或类似的东西.所以我想我会在这里问:ADL的缺陷是什么?
参数依赖查找存在一个巨大的问题.例如,考虑以下实用程序:
#includenamespace utility { template void print(T x) { std::cout << x << std::endl; } template void print_n(T x, unsigned n) { for (unsigned i = 0; i < n; ++i) print(x); } }
这很简单,对吧?我们可以调用print_n()
并传递任何对象,它将调用print
打印对象的n
次数.
实际上,事实证明,如果我们只看这个代码,我们完全不知道将调用哪个函数print_n
.它可能是print
这里给出的函数模板,但可能不是.为什么?依赖于参数的查找.
举个例子,假设你写了一个代表独角兽的类.出于某种原因,你还定义了一个名为print
(巧合!)的函数,它只是通过写入一个解除引用的空指针(谁知道你为什么这样做;这并不重要)导致程序崩溃:
namespace my_stuff { struct unicorn { /* unicorn stuff goes here */ }; std::ostream& operator<<(std::ostream& os, unicorn x) { return os; } // Don't ever call this! It just crashes! I don't know why I wrote it! void print(unicorn) { *(int*)0 = 42; } }
接下来,您编写一个创建独角兽的小程序并将其打印四次:
int main() { my_stuff::unicorn x; utility::print_n(x, 4); }
你编译这个程序,运行它,然后......它崩溃了."什么?!没办法,"你说:"我刚刚打电话print_n
,打电话print
给四面八国打印独角兽的功能!" 是的,这是真的,但它没有调用print
你期望它调用的函数.它被称为my_stuff::print
.
为什么my_stuff::print
选择?在名称查找期间,编译器会看到调用的参数print
是类型unicorn
,这是在命名空间中声明的类类型my_stuff
.
由于依赖于参数的查找,编译器在搜索名为的候选函数时包含此命名空间print
.它找到my_stuff::print
,然后在重载解析期间被选为最佳可行候选者:不需要转换来调用任一候选print
函数,并且非模板函数优先于函数模板,因此非模板函数my_stuff::print
是最佳匹配.
(如果您不相信这一点,可以按原样编译此问题中的代码并查看ADL的运行情况.)
是的,依赖于参数的查找是C++的一个重要特性.实际上,需要实现某些语言功能(如重载运算符)的所需行为(考虑流库).也就是说,它也非常非常有缺陷,可能导致非常丑陋的问题.已经有几个建议来修复依赖于参数的查找,但它们都没有被C++标准委员会接受.