Shouldn't there be a way to exclude member functions out of name lookup explicitly?

166 views
Skip to first unread message

Johannes Gerd Becker

unread,
Jul 27, 2015, 12:30:07 PM7/27/15
to

Currently, you cannot rely on ADL within a class defining a member
function of the same name as the function you would like to be called.

Suppose I want to define a container based on some underlying
container, and I want the underlying container to be able to be a
template parameter:

template <class UnderlyingContainer>
struct MyContainer {
UnderlyingContainer underlying;

/* ... */

iterator begin () { /* ... */ }

/* ... */

void someOtherMemberFunction () {
// Now I might want to write

auto i = begin (underlying);

// and rely on ADL,
// but that will fail, since the compiler will find the member function
}
};

A Stackoverflow question

http://stackoverflow.com/questions/13407205/calling-nonmember-instead-of-member-function

did not bring up a good solution.

Explicit qualification will not help me, as I do not know what the
type of the template parameter will be when the template is
instantiated.

Currently I have to create a base class without begin, define my
member function there and then inherit from the base class and add
begin in the subclass. That's ugly.

The problem would vanish if C++ had a way to say that I explicitly do
not want member functions to be considered (i.e., that this-> should
not be implicitly tried first for a particular function call).

The problem becomes the more severe the more you follow Herb Sutter's
advice to prefer nonmember over member functions.





--
[ comp.std.c++ is moderated. To submit articles, try posting with your ]
[ newsreader. If that fails, use mailto:std-cpp...@vandevoorde.com ]
[ --- Please see the FAQ before posting. --- ]
[ FAQ: http://www.comeaucomputing.com/csc/faq.html ]

Anton Bikineev

unread,
Jul 30, 2015, 1:00:07 PM7/30/15
to

You can just declare a free function outside of class like

inline Iterator begin_force_adl(Iterator& it)
{
auto p = begin(it);
}

And call it explicitly qualifying from your member function.

Not sure if it's what you were asking for.

Johannes Gerd Becker

unread,
Aug 1, 2015, 10:10:06 PM8/1/15
to

Of course, defining a helper function outside the class is a workaround (as
is first defining a base class, as described in my original post).

The point I would like to make, however, is another one.

There are workarounds, yet they get the more tedious the more often the
problem arises, namely in templated code. (To begin with: templates live
mostly in headers, so the helper functions must live in headers as well,
and break encapsulation, pollute my namespace or are simply inconvenient as
they have to be templates themselves).

It seems consensus (more or less) now that non-member functions defined in
the same namespace as a class are part of the interface of that class,
BECAUSE they will usually be found by ADL.

Now the rule that, within a member function of some class, say B, member
functions of B take absolute precendence when a name is looked up, has the
unwanted effect that some part of the interface of another, completely
unrelated class A, namely the nonmember functions that belong to the
interface of A, may become unusable from within B.

Why can I write

using namespace std;
auto i = begin (vec);

everywhere in my code, yet not within a class that has a member function
named begin ()?

I tempted to think this should be considered a design mistake.

One could think of several approaches to solve that problem:

1.) Introduce a clause like

using explicit this;

which would locally force explicitly using this-> when you want to access a
member. Then I would write

using namespace std;
using explicit this;
auto i = begin (vec); // ADL
auto j = this->begin (); // member

2.) Introduce a special qualifier (like, e.g., .::) to indicate that the
following name does *not* refer to a member function. Then I could write

using namespace std;
auto i = .::begin (vec); // ADL
auto j = begin (); // member

Jakob Bohm

unread,
Aug 12, 2015, 2:10:08 PM8/12/15
to

On 02/08/2015 05:09, Johannes Gerd Becker wrote:

>
> Of course, defining a helper function outside the class is a workaround (=
as
> is first defining a base class, as described in my original post).
>
> The point I would like to make, however, is another one.
>
> There are workarounds, yet they get the more tedious the more often the
> problem arises, namely in templated code. (To begin with: templates live
> mostly in headers, so the helper functions must live in headers as well,
> and break encapsulation, pollute my namespace or are simply inconvenient =
as
> they have to be templates themselves).
>
> It seems consensus (more or less) now that non-member functions defined i=
n
> the same namespace as a class are part of the interface of that class,
> BECAUSE they will usually be found by ADL.
>
> Now the rule that, within a member function of some class, say B, member
> functions of B take absolute precendence when a name is looked up, has th=
e
> unwanted effect that some part of the interface of another, completely
> unrelated class A, namely the nonmember functions that belong to the
> interface of A, may become unusable from within B.
>
> Why can I write
>
> using namespace std;
> auto i = begin (vec);
>
> everywhere in my code, yet not within a class that has a member function
> named begin ()?
>
> I tempted to think this should be considered a design mistake.
>
> One could think of several approaches to solve that problem:
>
> 1.) Introduce a clause like
>
> using explicit this;
>
> which would locally force explicitly using this-> when you want to access=
a
> member. Then I would write
>
> using namespace std;
> using explicit this;
> auto i = begin (vec); // ADL
> auto j = this->begin (); // member
>
> 2.) Introduce a special qualifier (like, e.g., .::) to indicate that the
> following name does *not* refer to a member function. Then I could write
>
> using namespace std;
> auto i = .::begin (vec); // ADL
> auto j = begin (); // member
>
>
Doesn't the existing :: qualifier do this already:

auto i == ::begin(vec); // ADL

Or did I miss some unfortunate interaction with the way named namespaces
were formally added to the language?


Enjoy

Jakob
--
Jakob Bohm, CIO, Partner, WiseMo A/S. https://www.wisemo.com
Transformervej 29, 2860 S=C3=B8borg, Denmark. Direct +45 31 13 16 10
This public discussion message is non-binding and may contain errors.
WiseMo - Remote Service Management for PCs, Phones and Embedded

Johannes Gerd Becker

unread,
Aug 16, 2015, 6:30:06 PM8/16/15
to

> > ...
> >
> > Why can I write
> >
> > using namespace std;
> > auto i = begin (vec);
> >
> > everywhere in my code, yet not within a class that has a member function
> > named begin ()?
> >
> > ...
>
> Doesn't the existing :: qualifier do this already:
>
> auto i == ::begin(vec); // ADL
>
> Or did I miss some unfortunate interaction with the way named namespaces
> were formally added to the language?
>
>
> Enjoy
>
> Jakob
> --
> Jakob Bohm, CIO, Partner, WiseMo A/S. https://www.wisemo.com
> Transformervej 29, 2860 S=C3=B8borg, Denmark. Direct +45 31 13 16 10
> This public discussion message is non-binding and may contain errors.
> WiseMo - Remote Service Management for PCs, Phones and Embedded

No, it doesn't.

::begin is considered as a qualified id, and ADL is not done for qualified
ids ([basic.lookup.argdep], paragraph 1). ::begin will always only refer to
the global namespace ([basic.lookup.qual], paragraph 4).

Best,
Johannes


--

Jakob Bohm

unread,
Aug 18, 2015, 11:40:08 PM8/18/15
to

On 17/08/2015 01:29, Johannes Gerd Becker wrote:

>
> ...
>>>
>>> Why can I write
>>>
>>> using namespace std;
>>> auto i = begin (vec);
>>>
>>> everywhere in my code, yet not within a class that has a member functio=
n
>>> named begin ()?
>>>
>>> ...
>>>
>>
>> Doesn't the existing :: qualifier do this already:
>>
>> auto i == ::begin(vec); // ADL
>>
>> Or did I miss some unfortunate interaction with the way named namespaces
>> were formally added to the language?
>>
>>
> No, it doesn't.
>
> ::begin is considered as a qualified id, and ADL is not done for qualifie=
d
> ids ([basic.lookup.argdep], paragraph 1). ::begin will always only refer =
to
> the global namespace ([basic.lookup.qual], paragraph 4).
>
>
Just to expand your statements for clarity, you seem to be saying two of
the following three things:

1. Qualified ids such as Foo::Bar(7, 'A') and ::Bar(7, 'A') are not
looked up based on their argument types, thus greatly reducing the
ability to invoke overloaded functions????

2. Qualified ids such as Foo::Bar(7, 'A') and ::Bar(7, 'A') are not
searched outside their specified scope/namespace, even if an outer
scope/namespace provides a more appropriate overloaded function????

3. The Global Scope prefix :: operator also excludes any non-class
namespaces brought in via the using keyword, including the "std"
namespace????

#3 is the unfortunate interaction I alluded to in my previous post.

If you are saying just #2 and #3, I am a bit confused what the original
problem was:

A. Unqualified ids such as Bar(7, 'A') are not searched beyond the
first found scope containing an identifier Bar, even if that
identifier has the wrong type for its arguments.

B. Unqualified ids such as Bar(7, 'A') are not searched beyond the
first found scope containing an identifier Bar, even if that
identifier has the wrong type for its arguments, but only if that
first found scope is a class declaration.

C. Limitation A or B applies only to some language implementations or
to some editions of the standard.

In either case, I am still wondering why the exemplified issue cannot be
handled directly with a Qualified Id, specifically which (2 or more) scopes
do you want the template implementation to search for identifier begin().


Enjoy

Jakob
--
Jakob Bohm, CIO, Partner, WiseMo A/S. https://www.wisemo.com
Transformervej 29, 2860 S=C3=B8borg, Denmark. Direct +45 31 13 16 10
This public discussion message is non-binding and may contain errors.
WiseMo - Remote Service Management for PCs, Phones and Embedded


Richard Smith

unread,
Aug 20, 2015, 2:20:08 AM8/20/15
to

On 27/07/15 18:22, Johannes Gerd Becker wrote:

Currently, you cannot rely on ADL within a class defining a member
> function of the same name as the function you would like to be called.
>

I must admit that my C++ is getting a bit rusty, but this "problem" was
well-known a decade ago, typically manifesting with std::swap and member
functions of the name. The solution then was equally well known, and
continues to work in C++11/14:

using std::begin;
auto i = begin(c);

Richard

Johannes Gerd Becker

unread,
Aug 21, 2015, 8:20:06 PM8/21/15
to
That's definitely a very nice solution, which mitigates the problem a lot.

Thank you very much for your most helpful posting.

I still think being able to write

using .::begin; // use non-member begin found by ADL
auto i = begin (v);

or

auto i = .::begin (v);

would be nice, as then I would not have to mention some begin in my code
that is perhaps never used. Yet I do have to admit that the problem now has
become more a cosmetic rather than a serious one.

Best regards,
Johannes
Reply all
Reply to author
Forward
0 new messages