【问题标题】:Call an inline friend from a method instead of itself从方法而不是自身调用内联朋友
【发布时间】:2021-08-13 22:41:59
【问题描述】:
struct P {
  int x, y;

  friend P operator-(P u, P v) { return {u.x - v.x, u.y - v.y}; }

  friend int cross(P u, P v) { return u.x * v.y - u.y * v.x; }
  int cross(P u, P v) const { return cross(u - *this, v - *this); }
};

该方法是一个无限循环(它将调用自己而不是朋友)。

有什么方法可以在不改变界面(名称)的情况下解决这个问题?

【问题讨论】:

  • 为什么必须内联定义朋友?是不是因为您希望它在普通名称查找中隐藏?
  • 当我运行你的代码时,我得到这个编译错误"error: no match for ‘operator-’ (operand types are ‘P’ and ‘const P’)"。你有任何编译错误吗?如果没有,你能告诉我们你的整个程序吗?
  • 在成员函数中,显式调用非成员(朋友)函数。例如,int cross(P u, P v) {return ::cross(u - *this, v - *this);}。 [注意:我假设这就是您想要的效果。]。
  • 在这种特殊情况下,朋友不需要访问 P 的内部。但是我没有看到一种简单的方法来定义全局之前(它需要声明 P)或之后(它破坏方法):/ @Peter 这就是我想要的,但编译器不会找到 ::与此修复交叉
  • 简单:不要调用成员函数cross。这真是令人困惑,如果您让我进行代码审查,它就会失败,没有如果或但是。为朋友留下名字cross,并为会员发明另一个名字。更好的是,完全放弃该成员并添加另一个具有 3 个参数的朋友。

标签: c++ friend


【解决方案1】:

可以在成员函数内部声明:

#include <stdio.h>

struct Foo {
  friend int add(int a, int b) {
    return a + b;
  }

  int add(int a, int b) const {
    int add(int, int); // function declaration
    return add(a, b);  // call that declared function
  }
};

int main() {
  Foo foo;
  printf("%d", foo.add(1, 2)); // prints 3
}

Demo

【讨论】:

  • 链接器在这个例子中没有找到add(int, int),但我会看看我是否可以调整它。另请注意,我需要朋友函数将 Foo 作为参数
  • @elbrunovsky 使用 GCC 11.0 或更高版本(或 clang 8.0 或更高版本)。从我分享的演示中可以看出,它可以工作。
【解决方案2】:

struct(或class)中不能有两个具有相同名称和相同签名的成员函数。

一种解决方案是将friend 函数移出struct,以便可以使用全局命名空间说明符:: 来引用它

#include <iostream>

int cross(int i) { return 42; }

struct P {
    friend int cross(int i);
    int cross(int i) const { return ::cross(i - 1); }
};

int main() {
    P p = {};
    std::cout << p.cross(7) << std::endl;
}

原始代码产生了两个警告,非常具有描述性:

1 > 1.cpp(6, 25) : warning C4514 : 'cross' : unreferenced inline function has been removed

1 > 1.cpp(7) : warning C4717 : 'P::cross' : recursive on all control paths, function will cause runtime stack overflow

回复:without changing the interface (the names) - 你可以简单地重命名你的好友函数,甚至将其设为私有;它不是界面的一部分。

【讨论】:

  • 您省略了将P 作为cross() 的参数的问题,但这可以通过将参数类型更改为(const)引用并在P 下方定义全局cross() 来解决(并添加几个前向声明)。 See it online
【解决方案3】:

Ayxan Haqverdili 的回答似乎是最简洁的,abides by the ISO。但是,pre-gcc 11.0 和 pre-clang 8.0 似乎有 cause this to fail 的错误。

幸运的是,在这种情况下,还有一个使用 argument-dependent lookup 的替代方法:

#include <iostream>

class Foo
{
    friend int Bar (Foo)
    {
        return 13;
    }
    friend int RedirectToBar (Foo);
    
public:

    int Bar (Foo)
    {
        return RedirectToBar(Foo());
    }
};

int RedirectToBar (Foo)
{
    return Bar(Foo());
}

int main ()
{
    std::cout << Foo().Bar(Foo()) << std::endl;
}

Live demo

请记住,这只是因为函数的某些参数是关联的类。

如果不是这样,并且您真的希望保持这种设计并且真的需要与旧的兼容编译器,然后我们可以通过将其添加为默认参数来添加一个虚拟关联类而不会破坏接口:

#include <iostream>

int RedirectToBar ();

class Foo
{
    struct Key {};

    friend int Bar (Key = Key())
    {
        return 13;
    }
    friend int RedirectToBar ();
    
public:

    int Bar ()
    {
        return RedirectToBar();
    }
};

int RedirectToBar ()
{
    return Bar(Foo::Key());
}

int main ()
{
    std::cout << Foo().Bar() << std::endl;
}

Live demo

但是,重要的是要补充一点,所有这些都是很大的代码气味。虽然我不知道 OP 的实际问题,但这似乎是一种绕过糟糕设计选择的可怕技巧。

【讨论】:

    【解决方案4】:

    我最终使用了类似于 Peter 和 Yksisarvinen 的解决方案:

    struct P {
        int x, y;
    
        friend P operator-(P u, P v) { return P{u.x - v.x, u.y - v.y}; }
    
        friend int cross(P u, P v);
        int cross(P u, P v) const;
    };
    
    int cross(P u, P v) { return u.x * v.y - u.y * v.x; }
    int P::cross(P u, P v) const { return ::cross(u - *this, v - *this); }
    

    如果我把它写在P 中,为什么编译器找不到::cross,还不太清楚。

    注意,有一个潜在的缺点:我们必须指定两个函数的返回类型(不能用auto 推断),但在这种情况下不需要这样做。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2010-09-27
      • 2016-06-10
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多