Why was argument dependent lookup invented?
Why was argument dependent lookup (ADL) invented? Is it just so we can write cout << stuff
instead of std::operator<<(cout, stuff)
? If that is the case, why wasn't ADL limited to operators instead of all functions?
Could the introduction of ADL have been prevented if C++ had had some other way to do ge开发者_开发问答neric output of both built-in and user-defined types, for example a type-safe printf
via variadic templates?
ADL was invented to allow the Interface Principle:
The Interface Principle
For a class X, all functions, including free functions, that both
- "mention" X, and
- are "supplied with" X
are logically part of X, because they form part of the interface of X.
Check out Herb Sutter's excellent Guru of the Week on the topic.
If that is the case, why wasn't ADL limited to operators instead of all functions?
Why limit it artificially? ADL may be tricky to implement (when combined with C++’ overloading rules)1 but it’s an extremely useful technique. For one thing, the fact that it also works on functions makes it much easier to use other namespaces without importing the whole namespace.
Case in point, the SeqAn library:
using seqan::String;
String<Char> url = "http://www.seqan.de/";
std::cout << "The URL " << url << " has length " << length(url) << std::endl;
Notice that I’ve used the function seqan::length
without qualifying its full name. ADL finds it anyway. The SeqAn library uses such namespace-scope functions excessively and prefixing every usage with the namespace name would be impractical. Likewise, importing the namespace often isn’t advisable.
The same is of course true for many other libraries, such as most of Boost’s.
1 And I believe that this wasn’t immediately realized by the committee.
Why not limit it to operators ?
Let's look at a simple generic algorithm:
template <typename FwdIt, typename T>
FwdIt remove(FwdIt first, FwdIt last, T const& value)
{
using std::swap;
FwdIt result = first;
for ( ; first != last; ++first)
if (!(*first == value)) swap(*result++, *first);
return result;
}
How does it work with custom types and their own version of swap
? Thanks to ADL.
This is what Sutter calls the Interface Principle, as Daniel mentioned.
It was invented to allow function polymorphism. The idea is that the function being a verb (like "print"), it have only one meaning. Still there can be different implementation depending on what the verb apply to (like int and float and std::string).
So we want one word for the concept but several implementation depending on what it's applied to.
What it's applied to is the argument(s). So we needed a way to use the same word on several différent type of arguments with - where needed- argument-type-relative implementation.
Try to write a complex concatenation with printInt(), printString(), printFloat() functions, you'll see the obvious verbosity.
The other reason is that it allows to check wich implementation are available for the givent argument type. If there is no implementation available (not even generic - using templates) then the compiler stop you as soon as he can and makes you know that it don't have an implementation of your verb for the given argument.
Yes, it was mostly invented for operators. But it also gives you the ability to incude nonmember functions into the interface of your class. And this is a very powerful thing which I like a lot. And this isn't limited to operators any more. For example, you could want to define a cross_product
function for your vector
class - you know what I mean :)
精彩评论