c++ - 是否可以用外部友元 lambda 函数覆盖虚函数?

标签 c++ gcc clang c++14

我正在编写一些游戏代码。我有一个抽象的游戏对象类,我想制作该类的许多不同实例。我想快速编写它们,而不是为它们每个编写子类,所以我尝试用外部 friend lambda 函数覆盖虚函数。这是一些简化的代码:

#include <iostream>
#include <memory>

class B {
public:
    virtual ~B() = default;
    virtual void g() = 0;
protected:
    int x_ = 42;
};

template<typename F>
class C : public B {
public:
    C(F f) : f_(f) {}
    void g() override { f_(*this); }
private:
    F f_;
    friend decltype(f_);
};

template<typename F>
std::unique_ptr<B> make_c(F f) {
    return std::make_unique<C<F>>(f);
}

int main() {
    std::unique_ptr<B> c = make_c([](auto& self) {
        std::cout << self.x_ << "\n";
    });
    c->g();

    return 0;
}

我用 Clang 和 GCC 尝试了这段代码。 Clang 编译它,但 GCC 死于内部编译器错误:

我尝试将 lambda 的参数类型从 auto& 更改为 B&。然后,GCC 和 Clang 都不会编译它,说“int B::x_ is protected within this context”。

我的代码有效吗?如果没有,是否存在一些替代方法?

为什么 Clang 编译它而 GCC 失败?

最佳答案

我真的不知道如何用 C++ 方法绕过 friend 的限制。但是请考虑以下几点:

int main(int argc, char* argv[])
{
    class : public B
    {
    public:
        virtual void g() override { std::cout << x_ << "\n"; }
    } c;
    c.g();

    return 0;
}

具有单个实例的匿名类。当然,这还不是你想要的,但它可以作为一些宏观魔法的基础......

#define MAKE_GENERIC_B(INSTANCE_NAME, IMPLEMENTATION) \
class : public B                                      \
{                                                     \
public:                                               \
    virtual void g() IMPLEMENTATION                   \
} INSTANCE_NAME

int main(int argc, char* argv[])
{
    MAKE_GENERIC_B(c,                             \
            {                                     \
                    char a[12] = { };             \
                    char d[10] = { };             \
                    memcpy(a, d, sizeof(d));      \
                    std::cout << x_ << std::endl; \
            }                                     \
    );
    c.g();
    return 0;
}

假设这与您所追求的非常接近。尽管如图所示的多行实现带有非常丑陋的行尾,但是如果您有许多适合一行的短实现,它可能会很有趣...

编辑:使用 std::unique_ptr:

#define CONCAT_(X, Y) X ## Y
#define CONCAT(X, Y) CONCAT_(X, Y)
#define MAKE_C() CONCAT(C, __LINE__)

#define MAKE_GENERIC_B(INSTANCE_NAME, IMPLEMENTATION) \
class MAKE_C() : public B                             \
{                                                     \
public:                                               \
    virtual void g() IMPLEMENTATION                   \
}; auto INSTANCE_NAME = std::make_unique<MAKE_C()>()

int main(int argc, char* argv[])
{
    MAKE_GENERIC_B(c,                             \
            {                                     \
                    char a[12] = { };             \
                    char d[10] = { };             \
                    memcpy(a, d, sizeof(d));      \
                    std::cout << x_ << std::endl; \
            }                                     \
    );
    c->g();

    MAKE_GENERIC_B(d, { std::cout << x_ * 2 << std::endl; });
    d->g();

    return 0;
}

由于我们不能在模板参数中定义类,所以我们需要提前定义它。为了获取个人名称,我使用了 __LINE__ 宏,另一种方法是提供一个类名作为宏参数。

此外,对于现在命名的类,您也可以提供一个构造函数,甚至可以扩展宏以向其提供参数,如果需要...

关于c++ - 是否可以用外部友元 lambda 函数覆盖虚函数?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/49175271/

相关文章:

c++ - 如何为算术表达式创建 BST

c++ - 如何将类方法的地址转换为 (void *)

c++ - strcpy_s 不适用于 gcc

visual-c++ - 您可以使用 clang-cl 编译扩展名为 .h 的 C++ 头文件吗?

clang 编译器能够将 OpenMP 区域卸载到 GPU

c++ - 实现内存管理模板类时未解析的外部符号

c++ - C++ 中的静态初始化顺序

C++ 使用统计

C++:std::visit 不能在 gcc 下编译

c++ - 可以使用 clang 将 C++17 与 CUDA 一起使用吗?