获取内联定义的友元函数的地址

时间:2017-05-25 17:06:02

标签: c++

请考虑以下代码:

#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给了我这个错误:

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
                       ^~~

这是因为bar是类本身定义的友元函数,使其在全局名称空间中不可见。访问它的唯一方法是通过ADL,但我还没有找到一种方法来使用ADL来获取bar的地址。

所以我的问题是,如何获取bar的地址?除了在bar之外定义foo之外还有其他方法吗?

3 个答案:

答案 0 :(得分:2)

您可以在不同的翻译单元中将封闭命名空间作用域中的friend函数声明:

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
  } 
};

foo.cpp

#include "foo.hpp"
void bar(foo);
void (*get_bar_address())(foo){
   return bar;
}

答案 1 :(得分:1)

似乎无法准确得到你想要的东西。问题是内联朋友声明的函数只能通过依赖于参数的查找来找到,如C ++标准11和14中所定义

  

<强> 7.3.1.2

     

3 在名称空间中首先声明的每个名称都是该名称空间的成员。如果非本地类中的友元声明首先声明一个类或函数,那么友元类或函数是最内层封闭命名空间的成员。在非命名查找(3.4.1)或限定查找(3.4.3)之前找不到朋友的名称,直到在该命名空间范围内提供匹配声明(在授予友谊的类定义之前或之后)。如果调用了友元函数,则可以通过名称查找找到其名称,该名称查找考虑名称空间中的函数和与函数参数类型相关联的类(3.4.2)。如果友元声明中的名称既不是限定语句也不是模板标识,并且声明是函数或详细类型说明符,则确定实体是否先前已声明的查找不应考虑最内层封闭命名空间之外的任何作用域

这意味着它只能通过参数依赖查找找到。修改规则,参数依赖查找仅应用于函数调用表达式中。因此,您将无法检索函数的地址,因为任何此类表达式都将评估为调用的结果。

如果你只需要一个指向具有与bar相同功能的函数的指针,你可以使用lambdas来获得指向这样一个函数的指针。

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

缺点是任何其他lambda都可能导致一个完全不同的地址。但是,如果唯一性很重要,您可以在不同的(静态)成员函数中提供地址。

答案 2 :(得分:0)

你试过这样的事吗?我觉得有趣的是你会在struct / class的范围内声明一个友元函数,但是嘿。

#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();
}

尝试了它并且它很有效LOL不确定原因:

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

虽然1的地址是可疑的。 :)但嘿它编译并做了一些事情。也许你可以更进一步,或者有人可以解释为什么这首先编译/工作。