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”而不是基类来声明回调
这是相互矛盾的。(第三个标准同样是为了方便客户机,因为每次手工向基类写出模板参数都会非常繁琐,不想这样做是使用“typedefs”的全部原因。)

我看到三种解决办法:

  • 放弃第一个或第三个标准

  • 某种丑陋而复杂的方案,我检查回调函数期望的参数类型,并验证它是基的伪typedef(通过检查sizeof是否相同,SFINAE是否从基派生,或者检查伪typedef生成宏插入的某种标记,或者类似的内容),然后执行强制转换

有更好的吗

(允许使用C++11。)

在模板参数中使用并传递派生类。对于回调,只需将
*此
强制转换为派生类型

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, ... );