Is it possible to have a non-friend function which can only be found by ADL?

209 views Asked by At

C++ has a feature, that in-class-defined friend functions can only be found by ADL (argument dependent lookup):

struct Foo {
    friend void fn(Foo) { } // fn can only be called by ADL, it won't be found by other lookup methods
};

Is it possible to achieve the same for non-friend functions? I ask this, because sometimes, I'd like to have this "only found by ADL" feature, but I don't actually need friend access to the class internals.

(And a somewhat opinionated question: if this is not possible, what is the reason for this? Is this "only found by ADL" rule intentionally designed into the language?)

2

There are 2 answers

0
StoryTeller - Unslander Monica On BEST ANSWER

It's only possible for friend functions, since they are the only functions for which this verbiage exists. Both the wording that makes functions invisible to ordinary name lookup, and the wording that considers them during ADL, is applied only to such friends.

This feature was actually introduced to replace another feature, that was deemed more problematic. N0777 is the paper detailing it. Templates used to inject their friend function names into scope when a template was instantiated, to be found by regular name lookup. That wasn't ideal and caused issues in overload resolution.

The feature was set to be removed, but it was the backbone of the Barton-Nackman trick, and so a solution was proposed that ultimately became what we know as inline friend functions being looked up by ADL only. It was tailored to facilitate the programming idiom.

Since no other wide-spread idiom that required such behavior was brought up since, this behavior wasn't extended to non-friend functions.

0
Vlad from Moscow On

The argument dependent lookup is introduced for unqualified name lookup of all functions not only of friend functions of classes.

Here is a demonstrative program

#include <iostream>

namespace N1
{

struct A
{
    int a = 10;
    friend void f( A &a ) { std::cout << "friend f( A & ) : A::a = " <<  a.a << '\n'; }     
};

void f( const A &a ) { std::cout << "f( const A & ) : A::a = " <<  a.a << '\n'; }   

}            

int main()
{
    N1::A a1;
    f( a1 );

    const N1::A a2;
    f( a2 );

    N1::f( a1 );
}    

The program output is

friend f( A & ) : A::a = 10
f( const A & ) : A::a = 10
f( const A & ) : A::a = 10

In this program when the qualified name lookup is used for a function with the name f and non-constant object a1 as its argument there is called the non-friend function f because the name of the friend function f is invisible in the namespace where its declaration is introduced.

Friend functions are included in the ADL because if a friend function is declared (and correspondingly defined) only in a class its name is invisible in the namespace where the declaration is introduced opposite to functions that declared in the namespace. So non-friend functions for example can be called using qualified names while friend functions that are only declared in classes can not be called using qualified names because they are invisible.

To achieve the same action with non-friend functions you have to make them at first invisible in the namespace where their declarations are introduced and the qualified name lookup shall not find them. It is impossible without introducing in the C++ Standard some new notions.