我正在写一些游戏代码。我有一个抽象的游戏对象类,我想创建该类的许多不同实例。我想快速编写它们,而不为每个子类编写子类,所以我尝试用外部朋友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失败时编译它?
答案 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__
宏,另一种方法是提供类名作为宏参数。
此外,对于现在命名的类,您也可以提供构造函数,甚至可以扩展宏以向其提供参数,如果需要的话......