C++ 使用继承而不是typedef实现前向声明性,并导致回调的复杂性
不太清楚这里的标题如何表达 问题是我有一个类模板:C++ 使用继承而不是typedef实现前向声明性,并导致回调的复杂性,c++,c++11,C++,C++11,不太清楚这里的标题如何表达 问题是我有一个类模板: template<various parameters> struct Base { ... }; 等等。在Base中回调函数的实现中,我想将*作为参数传递给回调函数。非常明智的是,C++不允许我这样做,因为在期望的派生类周围传递基类一般是不安全的。但在这种特定情况下,这不应该是一个问题,因为派生类不向基类添加任何内容,只是用来代替typedef 看来我有三个标准: 我希望“typedefs”是可向前声明的 我需要能够将基类传递
template<various parameters> struct Base { ... };
等等。在Base中回调函数的实现中,我想将*作为参数传递给回调函数。非常明智的是,C++不允许我这样做,因为在期望的派生类周围传递基类一般是不安全的。但在这种特定情况下,这不应该是一个问题,因为派生类不向基类添加任何内容,只是用来代替typedef
看来我有三个标准:
- 我希望“typedefs”是可向前声明的
- 我需要能够将基类传递给回调
- 我希望能够使用“typedefs”而不是基类来声明回调
- 放弃第一个或第三个标准
- 某种丑陋而复杂的方案,我检查回调函数期望的参数类型,并验证它是基的伪typedef(通过检查sizeof是否相同,SFINAE是否从基派生,或者检查伪typedef生成宏插入的某种标记,或者类似的内容),然后执行强制转换
*此
强制转换为派生类型
template<class Derived, other params...>
struct Base{
template<class F>
void do_callback_stuff(F func){
func(static_cast<Derived&>(*this), ....);
}
};
struct A : public Base<A, other args...>{};
模板
结构基{
模板
void do_callback_stuff(F func){
func(静态演员表(*本),…);
}
};
结构A:公共基{};
也许将奇怪的重复模板模式和隐式转换结合起来可以达到以下目的:
template<typename Derived, various parameters> struct Base
{
operator Derived&() { return static_cast<Derived&>(*this); }
operator Derived const&() const { return static_cast<Derived const&>(*this); }
other stuff;
};
struct A: Base<A, some arguments> {};
模板结构库
{
运算符派生的&({返回静态_cast(*this);}
运算符派生的常量&()常量{return static_cast(*this);}
其他东西;
};
结构A:Base{};
正如其他人所建议的,CRTP可能是一种解决方案
但您还应该重新考虑回调何时以及为什么需要引用一个对象,而该对象的专用调用者并不了解该对象的详细信息。我想说的是:“类模板在实例化之前不太了解自己的详细信息”
因此,检查回调可能具有的与调用者对象引用(实例化的类模板对象)相关的使用模式,并提取公共基类或基接口
然后从该基础派生类模板:
struct BaseInterface {
// HERE, declare anything, that callbacks may use
virtual void foo() = 0;
};
template<various parameters>
struct Base : public BaseInterface { ... };
void some_callback( BaseInterface& caller, ... );
struct-BaseInterface{
//在这里,声明回调可能使用的任何内容
虚拟void foo()=0;
};
模板
结构基:公共基接口{…};
取消某些_回调(BaseInterface和调用者,…);
吹毛求疵,但很重要:你不是从“it”中派生出来的,即来自模板。相反,您是从各种不同的、不相关的类Base
,Base
,等等中派生出来的。是的,我是从应用于各种参数的模板的各种实例化中派生出来的。这是我的意思。一个启发性的想法。但即使这样限制接口是有意义的(虽然没有,但你不可能知道),BaseInterface仍然需要以同样的方式进行模板化,我们回到了第一步。当然。这一次有一个明确答案的问题让人耳目一新。(尽管值得注意的是,这基本上是第三代的一个更干净、更有纪律的化身,接下来的问题,因为我仍然想对派生进行一些静态检查。派生(派生,Base)和sizeof(派生)==sizeof(Base)是静态_cast(Base&)的充分条件吗为了安全起见?@illissius:试试std::is_base_of::value
。它就在
中。这就足够了。如果您绝对想确保派生的
实际上是从基
派生的(应该是这样),请在类主体中放入一个简单的静态断言。模板类基{typedef Base this_type;static_assert(std::is_Base_of::value,“错误的派生模板参数!”);}
解决这一问题的机制不是问题。问题是,即使我们确实知道派生的确实是从基础派生的,但将基础转换为派生的通常是不安全的。问题是我们是否还添加了sizeof(派生)=sizeof(基础)的条件,这是否构成了cast安全的充分条件?或者是否存在其他可能的陷阱(除了缺少数据成员之外)?(这种情况是未定义的行为,但应该起作用,还是实际上在某个地方得到了标准的保证?)@伊利修斯:你希望otself使用Base
而不从中派生吗?这是我能想象的唯一一种情况,你不能强制转换到派生类,因为没有派生类……除此之外,如果派生类不添加任何成员,那么与大小的比较总是正确的。
template<typename Derived, various parameters> struct Base
{
operator Derived&() { return static_cast<Derived&>(*this); }
operator Derived const&() const { return static_cast<Derived const&>(*this); }
other stuff;
};
struct A: Base<A, some arguments> {};
struct BaseInterface {
// HERE, declare anything, that callbacks may use
virtual void foo() = 0;
};
template<various parameters>
struct Base : public BaseInterface { ... };
void some_callback( BaseInterface& caller, ... );