我正在编写一些游戏代码。我有一个抽象的游戏对象类,我想制作该类的许多不同实例。我想快速编写它们,而不是为它们每个编写子类,所以我尝试用外部 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 死于内部编译器错误:
- clang :https://wandbox.org/permlink/Kw82BdsKYEg0i2ln
- 海湾合作委员会:https://wandbox.org/permlink/F344XvvzO2JJH8Fa
我尝试将 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/