是否可以使用外部朋友lambda函数覆盖虚函数?

时间:2018-03-08 14:16:38

标签: c++ gcc clang c++14

我正在写一些游戏代码。我有一个抽象的游戏对象类,我想创建该类的许多不同实例。我想快速编写它们,而不为每个子类编写子类,所以我尝试用外部朋友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_在此上下文中受到保护”。

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

为什么Clang在GCC失败时编译它?

1 个答案:

答案 0 :(得分:1)

我真的没有看到如何用C ++手段解决朋友的限制。但请考虑以下事项:

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__宏,另一种方法是提供类名作为宏参数。

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