6

Consider the following code:

#include <iostream>

struct foo {
    friend void bar(foo) {}

    void foobar() {
        std::cout << &bar << '\n'; // error
    }
};

int main() {
    bar(foo{}); // ok, visible through ADL
    foo{}.foobar();
}

gcc gives me this error:

main.cpp: In member function 'void foo::foobar()':
main.cpp:7:23: error: 'bar' was not declared in this scope
         std::cout << &bar << '\n'; // error
                       ^~~

That's because bar is a friend function defined in the class itself, making it invisible in the global namespace. The only way to access it is through ADL, but I have not found a way to use ADL to get the address of bar.

So my question is, how can I take the address of bar? Is there any other way than to define bar outside of foo?

Rakete1111
  • 47,013
  • 16
  • 123
  • 162
  • 2
    Since ADL only applies for function calls, your question boils down to "how to retrieve the address of a function from a funtion call expression". I doubt there is a way, but surprises are welcome. – Quentin May 25 '17 at 17:15
  • Why? As the function is declared inline, it is more than likely to be inlined everywhere it is used. It therefore doesn't *have* a unique address. What is the outer problem here? – user207421 May 25 '17 at 22:37
  • @EJP The "problem" is that I have several operators each executing the same steps except a specific friend function. My goal was to call this generic function, instead of writing the same steps over and over again. But I needed to pass those friend functions as parameters, hence this question. – Rakete1111 May 26 '17 at 10:24

3 Answers3

2

You could declare the friend function in the enclosing namespace scope in a different translation unit:

In foo.hpp:

#include <iostream>
struct foo;
void (*get_bar_address())(foo);
struct foo {
  friend void bar(foo) {}

  void foobar() {
      std::cout << get_bar_address() << '\n'; // no error
  } 
};

In foo.cpp

#include "foo.hpp"
void bar(foo);
void (*get_bar_address())(foo){
   return bar;
}
Oliv
  • 17,610
  • 1
  • 29
  • 72
1

It seems to be impossible to get exactly what you want. The problem is that an inline friend declared function will only be found by argument-dependant lookup, as defined in the C++ standard 11 and 14 in

7.3.1.2

3 Every name first declared in a namespace is a member of that namespace. If a friend declaration in a non-local class first declares a class or function the friend class or function is a member of the innermost enclosing namespace. The name of the friend is not found by unqualified lookup (3.4.1) or by qualified lookup (3.4.3) until a matching declaration is provided in that namespace scope (either before or after the class definition granting friendship). If a friend function is called, its name may be found by the name lookup that considers functions from namespaces and classes associated with the types of the function arguments (3.4.2). If the name in a friend declaration is neither qualified nor a template-id and the declaration is a function or an elaborated-type-specifier, the lookup to determine whether the entity has been previously declared shall not consider any scopes outside the innermost enclosing namespace.

That means it is only ever be found by argument dependent lookup. Revising the rules, argument dependent lookup is only applied within a function call expression. Hence you will not be able to retrieve the address of the function as any such an expression will evaluate to the result of the call.


In case you only need a pointer referring to a function with the same functionality as bar, you could however utilize lambdas to get a pointer to such a function nevertheless.

void foo::foobar() {
    using fntype = void(*)(foo);
    std::cout << (fntype)[](foo f){ bar(f); } << '\n'; // error
}

The downside is that any other lambda will likely result in an intirely different address. You could however offer the address in a different (static) member function if the uniqueness is of importance.

Community
  • 1
  • 1
HeroicKatora
  • 958
  • 8
  • 17
0

Did you try something like this? I find it interesting that you would declare a friend function from inside the scope of the struct/class but hey.

#include <iostream>

// forward declare foo so you can forward declare bar :)
struct foo;

// forward declare bar so an address is available on the line that references it.
void bar(foo);

struct foo 
{
    friend void bar(foo)
    {
       std::cout << "bar called" << std::endl;
    }

    void foobar() 
    {
        std::cout << "address of bar " << &bar << '\n'; // error
    }
};

int main() 
{
    bar(foo{}); // ok, visible through ADL
    foo{}.foobar();
}

Tried it and it works LOL not sure why:

$ ./test.exe
bar called
address of bar 1

though address of 1 is suspicious. :) but hey it compiled and did something. maybe you can take it further or someone can explain why this compiled/worked in the first place.

Rob
  • 2,618
  • 2
  • 22
  • 29
  • 2
    It works because now the name `bar` is visible in the global namespace, and so it is legal to reference it anywhere, including taking its address :) But I was looking for a solution so that I don't have to declare and/or define it outside of `foo`. – Rakete1111 May 25 '17 at 17:34
  • 3
    The address is not 1, but the overload of `operator<<` for a `bool` is taken, and any non 0 value is `1` or `true`. Thanks though! :) – Rakete1111 May 25 '17 at 17:35
  • I figured it could not be that simple :) – Rob May 25 '17 at 17:35
  • @Rakete1111 The reason I asked about the friend part is that my understand is that friend declares the signature of an externally non member function as a friend to the class/struct but if the function is defined inside the scope of the class/struct what is the advantage? does is not add that function to the class/struct vtable or something? – Rob May 25 '17 at 17:51
  • Rule of thumb: If the function is not `virtual`, no vtable is involved. See this question for the advantages https://stackoverflow.com/questions/381164/friend-and-inline-method-whats-the-point – Rakete1111 May 25 '17 at 17:53
  • Sorry I meant not part of the class :( and I answered my own question with a quick test.. no it is not part of the class. – Rob May 25 '17 at 17:55