【问题标题】:Getting the address of an inline defined friend function获取内联定义的友元函数的地址
【发布时间】:2017-10-26 10:22:09
【问题描述】:

考虑以下代码:

#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 的地址?除了在foo 之外定义bar,还有其他方法吗?

【问题讨论】:

  • 由于 ADL 仅适用于函数调用,因此您的问题归结为“如何从函数调用表达式中检索函数的地址”。我怀疑有办法,但欢迎惊喜。
  • 为什么?由于该函数被声明为内联,因此很可能在任何使用它的地方都被内联。因此它没有唯一的地址。这里的外部问题是什么?
  • @EJP “问题”是我有几个运算符,每个运算符都执行相同的步骤,除了一个特定的朋友函数。我的目标是调用这个通用函数,而不是一遍又一遍地编写相同的步骤。但是我需要将这些朋友函数作为参数传递,因此提出了这个问题。

标签: c++


【解决方案1】:

你尝试过这样的事情吗?我发现你会在结构/类的范围内声明一个友元函数很有趣,但是嘿。

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

试过了,效果很好,不知道为什么:

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

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

【讨论】:

  • 之所以有效,是因为现在名称 bar 在全局命名空间中可见,因此在任何地方引用它都是合法的,包括获取其地址 :) 但我一直在寻找解决方案,以便我不必在 foo 之外声明和/或定义它。
  • 地址不是1,而是operator&lt;&lt;bool的重载,任何非0值都是1true。不过谢谢! :)
  • 我觉得不可能这么简单:)
  • @Rakete1111 我询问朋友部分的原因是,我的理解是,朋友将外部非成员函数的签名声明为类/结构的朋友,但如果该函数是在类/结构的范围有什么优势?是不是将该函数添加到类/结构 vtable 或其他东西中?
  • 经验法则:如果函数不是virtual,则不涉及vtable。看到这个问题的优势stackoverflow.com/questions/381164/…
【解决方案2】:

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

7.3.1.2

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

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


如果您只需要一个指向与bar 具有相同功能的函数的指针,您仍然可以使用 lambdas 来获取指向此类函数的指针。

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

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

【讨论】:

    【解决方案3】:

    您可以在不同翻译单元的封闭命名空间范围内声明友元函数:

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

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2016-11-14
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2020-04-28
      • 1970-01-01
      • 2014-02-18
      • 1970-01-01
      相关资源
      最近更新 更多