Name hiding by using declaration - c++

#include <iostream>
struct H
{
void swap(H &rhs);
};
void swap(H &, H &)
{
std::cout << "swap(H &t1, H &t2)" << std::endl;
}
void H::swap(H &rhs)
{
using std::swap;
swap(*this, rhs);
}
int main(void)
{
H a;
H b;
a.swap(b);
}
And this is the result:
swap(H &t1, H &t2)
In the code above, I try to define a swap function of H. In the function void H::swap(H &rhs), I use an using declaration to make the name std::swap visible. If there isn't an using declaration, the code cannot be compiled because there is no usable swap function with two parameters in class H.
I have a question here. In my opinion, after I used the using declaration -- using std::swap, it just make the std::swap -- the template function in STL visible. So I thought that the swap in STL should be invoked in H::swap(). But the result showed that the void swap(H &t1, H &t2) was invoked instead.
So here is my question:
Why can't I invoke swap without a using declaration?(I guess it is because there is no swap function with two parameters in the class. But I am not sure. )
Why will the swap of my definition be invoked instead of the STL swap in the H::swap?

Why can't I invoke swap without a using declaration?
We start in the nearest enclosing scope and work our way outwards until we find something. With this:
void H::swap(H &rhs)
{
swap(*this, rhs);
}
Unqualified swap finds H::swap(). Then we do argument-dependent lookup. But the rule there is, from [basic.lookup.argdep]:
Let X be the lookup set produced by unqualified lookup (3.4.1) and let Y be the lookup set produced by
argument dependent lookup (defined as follows). If X contains
— a declaration of a class member, or
— a block-scope function declaration that is not a using-declaration, or
— a declaration that is neither a function or a function template
then Y is empty. Otherwise Y is the set of declarations found in the namespaces associated with the argument types as described below. [...]
Since the unqualified lookup set finds a class member, the argument-dependent lookup set is empty (that is, it doesnt find swap(H&, H&)).
Why will the swap of my definition be invoked instead of the STL swap in the H::swap?
When you add:
void H::swap(H &rhs)
{
using std::swap;
swap(*this, rhs);
}
now unqualified swap finds std::swap() and not H::swap(), since the former is declared in a more inner scope. using std::swap; does not match any of the criteria in the above-stated rule that would lead to Y being empty (it's not a class member, it is a using-declaration, and it is a function template). As a result, the argument-dependent lookup set does include declarations found in associated namespaces - which includes swap(H&, H&) (since H is in the global namespace). We end up with two overload candidates - and yours is preferred since it's the non-template.
See Xeo's answer on the preferred way to add swap to your class. Basically, you want to write:
struct H {
friend void swap(H&, H&) { ... }
};
This will be found by ADL (and only by ADL). And then whenever anybody calls swap correct:
using std::swap;
swap(a, b);
Lookup will find yours where appropriate.

Related

What is a niebloid?

With C++20 we can read the term "niebloid" more often now in the cppreference.
On SO we can find today 2020/07/16 2 articles mentioning it:
First post
Second post, talking about customization point objects
Google does also not spit out that many results. The most prominent is maybe here.
Can somebody shed a little bit more light on niebloids?
The term niebloid comes from Eric Niebler's name. In simple words, they are function objects that disable ADL (argument-dependent lookup) from happening so that the overloads in std:: aren't picked up when an algorithm from std::ranges is called.
Here's a tweet (from 2018) and answer from Eric himself suggesting the name. Eric wrote an article in 2014 explaining this concept.
It can best be seen in action in the standard document itself:
25.2.2
The entities defined in the std​::​ranges namespace in this Clause are not found by argument-dependent name lookup (basic.lookup.argdep).
When found by unqualified (basic.lookup.unqual) name lookup for the postfix-expression in a function call, they inhibit argument-dependent name lookup.
void foo() {
using namespace std::ranges;
std::vector<int> vec{1,2,3};
find(begin(vec), end(vec), 2); // #1
}
The function call expression at #1 invokes std​::​ranges​::​find, not std​::​find, despite that (a) the iterator type returned from begin(vec) and end(vec) may be associated with namespace std and (b) std​::​find is more specialized ([temp.func.order]) than std​::​ranges​::​find since the former requires its first two parameters to have the same type.
The above example has ADL turned off, so the call goes directly to std::ranges::find.
Let's create a small example to explore this further:
namespace mystd
{
class B{};
class A{};
template<typename T>
void swap(T &a, T &b)
{
std::cout << "mystd::swap\n";
}
}
namespace sx
{
namespace impl {
//our functor, the niebloid
struct __swap {
template<typename R, typename = std::enable_if_t< std::is_same<R, mystd::A>::value > >
void operator()(R &a, R &b) const
{
std::cout << "in sx::swap()\n";
// swap(a, b);
}
};
}
inline constexpr impl::__swap swap{};
}
int main()
{
mystd::B a, b;
swap(a, b); // calls mystd::swap()
using namespace sx;
mystd::A c, d;
swap(c, d); //No ADL!, calls sx::swap!
return 0;
}
Description from cppreference:
The function-like entities described on this page are niebloids, that is:
Explicit template argument lists may not be specified when calling any of them.
None of them is visible to argument-dependent lookup.
When one of them is found by normal unqualified lookup for the name to the left of the function-call operator, it inhibits argument-dependent lookup.
Niebloids aren't visible to argument dependent lookup(ADL) because they are function objects, and ADL is done only for free functions and not function objects. The third point is what happened in the example from the standard:
find(begin(vec), end(vec), 2); //unqualified call to find
The call to find() is unqualified, so when lookup starts, it finds std::ranges::find function object which in turn stops ADL from happening.
Searching some more, I found this which, in my opinion is the most understandable explanation of niebloids and CPOs (customization point objects):
... a CPO is an object (not a function); it’s callable; it’s constexpr-constructible, [...] it’s customizable (that’s what it means to “interact with program-defined types”); and it’s concept-constrained.
[...]
If you remove the adjectives “customizable, concept-constrained” from the above, then you have a function object that turns off ADL — but is not necessarily a customization point. The C++2a Ranges algorithms, such as std::ranges::find, are like this. Any callable, constexpr-constructible object is colloquially known as a “niebloid,” in honor of Eric Niebler.
From cppreference:
The function-like entities described on this page are niebloids, that is:
Explicit template argument lists may not be specified when calling any of them.
None of them is visible to argument-dependent lookup.
When one of them is found by normal unqualified lookup for the name to the left of the function-call operator, it inhibits argument-dependent lookup.
In practice, they may be implemented as function objects, or with special compiler extensions.

Namespace resolution with operator== in the STL

Consider a simple type, in a namespace, with an operator==:
namespace ANamespace {
struct Foo { int i; float f; };
}
#ifdef INSIDE
namespace ANamespace {
bool operator==(const Foo& l, const Foo& r)
{
return l.i == r.i && l.f == r.f;
}
}
#else
bool operator==(const ANamespace::Foo& l, const ANamespace::Foo& r)
{
return l.i == r.i && l.f == r.f;
}
#endif
bool compareElements(const std::vector<ANamespace::Foo>& l, const std::vector<ANamespace::Foo>& r)
{
return l == r;
}
If operator== is defined inside ANamespace (by defining INSIDE), the example compiles. But if operator== is defined in the global namespace (the #else case), the function compareElements() doesn't compile - both in GCC and Clang, and with both libstdc++ and libc++. All emit a template error along the lines of:
In file included from /usr/bin/../lib64/gcc/x86_64-pc-linux-gnu/9.2.0/../../../../include/c++/9.2.0/vector:60:
/usr/bin/../lib64/gcc/x86_64-pc-linux-gnu/9.2.0/../../../../include/c++/9.2.0/bits/stl_algobase.h:820:22: error: invalid operands to binary expression ('const ANamespace::Foo' and 'const ANamespace::Foo')
if (!(*__first1 == *__first2))
~~~~~~~~~ ^ ~~~~~~~~~
...
However, directly comparing two Foos in a function, e.g.,
bool compareDirectly(const ANamespace::Foo& l, const ANamespace::Foo& r)
{
return l == r;
}
seems to work fine regardless of where operator== is defined.
Are there rules in the standard about where the STL expects operator== to be defined?
!(*__first1 == *__first2) takes place in std::operator==, a function template, so it is considered a dependent unqualified function call expression, so during overload resolution only functions found within the definition context of std::operator== and those found via ADL are candidates.
Clearly there are no operator==(const Foo&, const Foo&) declared within the context of the definition of the standard comparison operator. In an argument dependent lookup (ADL) the namespaces of each of the arguments are checked to search for a viable function for the call, so this is why defining operator== inside of ANamespace works.
In short, declaring operator== in the same namespace in which your class is declared guarantees that argument-dependent lookup will find it, so that's what you should do. The standard does not mandate that you follow this convention, but in practice it is the only way to obtain the guarantee. This also applies to other operators that the standard library might invoke on your types.
If you choose to declare operator== in the global namespace but your type is not declared in the global namespace, there is a chance that the standard library algorithm will still be able to find your operator== through unqualified name lookup. However, there's no guarantee that this works, since unqualified name lookup will stop at the innermost enclosing namespace in which operator== is found. In other words, in an algorithm of the form
namespace std {
template< class InputIt1, class InputIt2 >
constexpr bool equal( InputIt1 first1, InputIt1 last1,
InputIt2 first2 ) {
// ...
}
}
the unqualified name lookup of operator== will find any operator==s declared in the std namespace (which of course, will not be applicable to your user-defined type) and then, if it found anything in std, even though it may not be a viable overload, will not look in the global namespace.
You need to read up on "ADL" aka "Argument Dependent Lookup".
Basically, when you write v1 == v2, the compiler looks for an operator== taking two arguments of the correct type ANamespace::Foo in the current namespace. (Note: We're ignoring conversions here). If it can't find one, then it will look in the namespace that the type is defined in (ANamespace).
Wikipedia has an article about this.

Unqualified name lookup applied instead of argument-dependent name lookup

Consider an example from the standard sec 3.4.1/3:
typedef int f;
namespace N
{
struct A
{
friend void f(A &);
operator int();
void g(A a)
{
int i = f(a);// f is the typedef, not the friend
// function: equivalent to int(a)
}
};
}
f(a) is postfix expression. How does compiler determine that f(a) is not a function call? I'm wondering when we have no error like f previously declared of typedef int f; as in the following example:
#include <stdio.h>
typedef int foo; //error: previous declaration of ‘typedef int foo’
class B
{
public:
friend void foo(B b){ printf("3"); } //error: ‘void foo(B)’ redeclared as different kind of symbol
static const int c=42;
};
int main(){ }
(In my version of the C++11 document the example is presented in 3.4.1/3).
The 3.4.1/3 clearly states that for the purposes of parsing, in order to perform the initial determination of whether this is a postfix expression or function call, a usual name lookup is performed. "Usual" means that the lookup is performed as described in the rest of 3.4.1 and no ADL is used at that initial stage. 3.4.1/3 explicitly says that "The rules in 3.4.2 have no effect on the syntactic interpretation of an expression." (3.4.2 is ADL).
In this example, while parsing f(a) the usual lookup is used to look for name f. It finds the global typedef-name ::f and nothing else. This means that f(a) is treated as a postfix expression (a cast), not as a function call. Note, that friend declaration of function f inside A refers to function N::f, but it does not introduce a declaration of N::f into N. Since N::f function is not explicitly declared in N (it is not visible in N), the usual lookup does not see it. It only sees the global ::f, which is a typedef-name.
If you want the usual name lookup to find the function in the first example, you have to declare that function in N explicitly
typedef int f;
namespace N
{
struct A; // <- added
void f(A &); // <- added
struct A
{
friend void f(A &);
...
Now the declaration of N::f is visible in N. Now the usual name lookup will find N::f and treat that f(a) as a function call from the very beginning.
Your second example is seriously different from the first. You have no extra namespace there. Because of that the friend function declaration in B refers to the global ::foo and claims that foo is a function. But the global ::foo is already declared as a typedef-name. This contradiction is exactly what causes the error.
(Amusingly, C++03 version of the standard contained an example in 3.4.1/3, which was essentially equivalent to your second example. I.e. the example in the standard was ill-formed. This is reported as Defect #139 of the standard.)

Why will two-phase lookup fail to choose overloaded version of 'swap'?

I am studying this fascinating answer to a subtle question regarding the best practice to implement the swap function for user-defined types. (My question was initially motivated by a discussion of the illegality of adding types to namespace std.)
I will not re-print the code snippet from the above-linked answer here.
Instead, I would like to understand the answer.
The answer I've linked above states, beneath the first code snippet, in regards to overloading swap in namespace std (rather than specializing it in that namespace):
If your compiler prints out something different then it is not
correctly implementing "two-phase lookup" for templates.
The answer then goes on to point out that specializing swap in namespace std (as opposed to overloading it) produces a different result (the desired result in the case of specialization).
However, the answer proceeds with an additional case: specializing swap for a user-defined template class - in which case, again, the desired result is not achieved.
Unfortunately, the answer simply states the facts; it does not explain why.
Can someone please elaborate on that answer, and describe the process of lookup in the two specific code snippets provided in that answer:
overloading swap in namespace std for a user-defined non-template class (as in the first code snippet of the linked answer)
specializing swap in namespace std for a user-defined template class (as in the final code snippet of the linked answer)
In both cases, the generic std::swap is called, rather than the user-defined swap. Why?
(This will shed light on the nature of two-phase lookup, and the reason for the best practice for implementing user-defined swap; thanks.)
Preamble with plenty of Standardese
The call to swap() in the example entails a dependent name because its arguments begin[0] and begin[1] depend on the template parameter T of the surrounding algorithm() function template. Two-phase name lookup for such dependent names is defined in the Standard as follows:
14.6.4.2 Candidate functions [temp.dep.candidate]
1 For a function call where the postfix-expression is a dependent name,
the candidate functions are found using the usual lookup rules (3.4.1,
3.4.2) except that:
— For the part of the lookup using unqualified name lookup (3.4.1), only function declarations from the template definition
context are found.
— For the part of the lookup using associated
namespaces (3.4.2), only function declarations found in either the
template definition context or the template instantiation context are
found.
Unqualified lookup is defined by
3.4.1 Unqualified name lookup [basic.lookup.unqual]
1 In all the cases listed in 3.4.1, the scopes are searched for a
declaration in the order listed in each of the respective categories;
name lookup ends as soon as a declaration is found for the name. If no
declaration is found, the program is ill-formed.
and argument-dependent lookup (ADL) as
3.4.2 Argument-dependent name lookup [basic.lookup.argdep]
1 When the postfix-expression in a function call (5.2.2) is an
unqualified-id, other namespaces not considered during the usual
unqualified lookup (3.4.1) may be searched, and in those namespaces,
namespace-scope friend function or function template declarations
(11.3) not otherwise visible may be found. These modifications to the
search depend on the types of the arguments (and for template template
arguments, the namespace of the template argument).
Applying the Standard to the example
The first example calls exp::swap(). This is not a dependent name and does not require two-phase name lookup. Because the call to swap is qualified, ordinary lookup takes place which finds only the generic swap(T&, T&) function template.
The second example (what #HowardHinnant calls "the modern solution") calls swap() and also has an overload swap(A&, A&) in the same namespace as where class A lives (the global namespace in this case). Because the call to swap is unqualified, both ordinary lookup and ADL take place at the point of definition (again only finding the generic swap(T&, T&)) but another ADL takes place at the point of instantiation (i.e where exp::algorithm() is being called in main()) and this picks up swap(A&, A&) which is a better match during overload resolution.
So far so good. Now for the encore: the third example calls swap() and has a specialization template<> swap(A&, A&) inside namespace exp. The lookup is the same as in the second example, but now ADL does not pick up the template specialization because it is not in an associated namespace of class A. However, even though the specialization template<> swap(A&, A&) does not play a role during overload resolution, it is still instantiated at the point of use.
Finally, the fourth example calls swap() and has an overload template<class T> swap(A<T>&, A<T>&) inside namespace exp for template<class T> class A living in the global namespace. The lookup is the same as in the third example, and again ADL does not pick up the overload swap(A<T>&, A<T>&) because it is not in an associated namespace of the class template A<T>. And in this case, there is also no specialization that has to be instantiated at the point of use, so the generic swap(T&, T&) is being callled here.
Conclusion
Even though you are not allowed to add new overloads to namespace std, and only explicit specializations, it would not even work because of the various intricacies of two-phase name lookup.
It is impossible to overload swap in namespace std for a user defined type. Introduction an overload (as opposed to a specialization) in namespace std is undefined behavior (illegal under the standard, no diagnosis required).
It is impossible to specialize a function in general for a template class (as opposed to a template class instance -- ie, std::vector<int> is an instance, while std::vector<T> is the entire template class). What appears to be a specialization is actually an overload. So the first paragraph applies.
The best practice for implementing user-defined swap is to introduce a swap function or overload in the same namespace as your template or class lives in.
Then, if swap is called in the right context (using std::swap; swap(a,b);), which is how it is called in std library, ADL will kick in, and your overload will be found.
The other option is to do a full specialization of swap in std for your particular type. This is impossible (or impractical) for template classes, as you need to specialize for each and every instance of your template class that exists. For other classes, it is fragile, as specialization applies to only that particular type: subclasses will have to be respecialized in std as well.
In general, specialization of functions is extremely fragile, and you are better off introducing overrides. As you cannot introduce overrides into std, the only place they will be reliably found from is in your own namespace. Such overrides in your own namespace are preferred over overrides in std as well.
There are two ways to inject a swap into your namespace. Both work for this purpose:
namespace test {
struct A {};
struct B {};
void swap(A&, A&) { std::cout << "swap(A&,A&)\n"; }
struct C {
friend void swap(C&, C&) { std::cout << "swap(C&, C&)\n"; }
};
void bob() {
using std::swap;
test::A a, b;
swap(a,b);
test::B x, y;
swap(x, y);
C u, v;
swap(u, v);
}
}
void foo() {
using std::swap;
test::A a, b;
swap(a,b);
test::B x, y;
swap(x, y);
test::C u, v;
swap(u, v);
test::bob();
}
int main() {
foo();
return 0;
}
the first is to inject it into the namespace directly, the second is to include it as an inline friend. The inline friend for "external operators" is a common pattern that basically means you can only find swap via ADL, but in this particular context does not add much.

Ambiguous call to templated function due to ADL

I've been bitten by this problem a couple of times and so have my colleagues. When compiling
#include <deque>
#include <boost/algorithm/string/find.hpp>
#include <boost/operators.hpp>
template< class Rng, class T >
typename boost::range_iterator<Rng>::type find( Rng& rng, T const& t ) {
return std::find( boost::begin(rng), boost::end(rng), t );
}
struct STest {
bool operator==(STest const& test) const { return true; }
};
struct STest2 : boost::equality_comparable<STest2> {
bool operator==(STest2 const& test) const { return true; }
};
void main() {
std::deque<STest> deq;
find( deq, STest() ); // works
find( deq, STest2() ); // C2668: 'find' : ambiguous call to overloaded function
}
...the VS9 compiler fails when compiling the second find. This is due to the fact that STest2 inherits from a type that is defined in boost namespace which triggers the compiler to try ADL which finds boost::algorithm::find(RangeT& Input, const FinderT& Finder).
An obvious solution is to prefix the call to find(…) with "::" but why is this necessary? There is a perfectly valid match in the global namespace, so why invoke Argument-Dependent Lookup? Can anybody explain the rationale here?
ADL isn't a fallback mechanism to use when "normal" overload resolution fails, functions found by ADL are just as viable as functions found by normal lookup.
If ADL was a fallback solution then you might easily fall into the trap were a function was used even when there was another function that was a better match but only visible via ADL. This would seem especially strange in the case of (for example) operator overloads. You wouldn't want two objects to be compared via an operator== for types that they could be implicitly converted to when there exists a perfectly good operator== in the appropriate namespace.
I'll add the obvious answer myself because I just did some research on this problem:
C++03 3.4.2
§2 For each argument type T in the function call, there is a set of zero or more associated namespaces [...] The sets of namespaces and classes are determined in the following way:
[...]
— If T is a class type (including unions), its associated classes are: the class itself; the class of which it is a
member, if any; and its direct and indirect base classes. Its associated namespaces are the namespaces
in which its associated classes are defined.
§ 2a If the ordinary unqualified lookup of the name finds the declaration of a class member function, the associated
namespaces and classes are not considered. Otherwise the set of declarations found by the lookup of
the function name is the union of the set of declarations found using ordinary unqualified lookup and the set
of declarations found in the namespaces and classes associated with the argument types.
At least it's standard conformant, but I still don't understand the rationale here.
Consider a mystream which inherits from std::ostream. You would like that your type would support all the << operators that are defined for std::ostream normally in the std namespace. So base classes are associated classes for ADL.
I think this also follows from the substitution principle - and functions in a class' namespace are considered part of its interface (see Herb Sutter's "What's in a class?"). So an interface that works on the base class should remain working on a derived class.
You can also work around this by disabling ADL:
(find)( deq, STest2() );
I think you stated the problem yourself:
in the global namespace
Functions in the global namespace are considered last. It's the most outer scope by definition. Any function with the same name (not necessarily applicable) that is found in a closer scope (from the call point of view) will be picked up first.
template <typename Rng, typename T>
typename Rng::iterator find( Rng& rng, T const& t );
namespace foo
{
bool find(std::vector<int> const& v, int);
void method()
{
std::deque<std::string> deque;
auto it = find(deque, "bar");
}
}
Here (unless vector or deque include algorithm, which is allowed), the only method that will be picked up during name look-up will be:
bool foo::find(std::vector<int> const&, int);
If algorithm is somehow included, there will also be:
template <typename FwdIt>
FwdIt std::find(FwdIt begin, FwdIt end,
typename std::iterator_traits<FwdIt>::value_type const& value);
And of course, overload resolution will fail stating that there is no match.
Note that name-lookup is extremely dumb: neither arity nor argument type are considered!
Therefore, there are only two kinds of free-functions that you should use in C++:
Those which are part of the interface of a class, declared in the same namespace, picked up by ADL
Those which are not, and that you should explicitly qualified to avoid issues of this type
If you fall out of these rules, it might work, or not, depending on what's included, and that's very awkward.