从模板参数继承并在c ++中向上转发

bou*_*aet 2 c++ polymorphism templates casting crtp

我曾尝试在VS2008中使用此代码(并且可能在示例中包含了太多上下文...):

class Base
{
    public:
    void Prepare() {
       Init();
       CreateSelectStatement();
       // then open a recordset
    }
    void GetNext() { /* retrieve next record */ }
    private:
    virtual void Init() = 0;
    virtual string CreateSelectStatement() const = 0;
};
class A : public Base
{
   public:
   int foo() { return 1; }
   private:
   virtual void Init() { /* init logic */ }
   virtual string CreateSelectStatement() { /* return well formed query */ }
};

template<typename T> class SomeValueReader : protected T
{
   public:
   void Prepare() { T::Prepare(); }
   void GetNext() { T::GetNext(); }
   T& Current() { return *this; } // <<<<<<<< this is where it is interesting
   SomeValue Value() { /* retrieve values from the join tables */ }
   private :
   string CreateSelectStatement() const
   {
   // special left join selection added to T statement
   }
};

void reader_accessAmemberfunctions_unittest(...)
{
   SomeValueReader<A> reader();
   reader.Prepare();
   reader.GetNext();
   A a = reader.Current();
   int fooresult = a.foo();
   // reader.foo()            >> ok, not allowed
   Assert::IsEqual<int>( 1, fooresult );
};
Run Code Online (Sandbox Code Playgroud)

这按预期工作,即访问"A"成员函数和fooresult返回1.但是,在unittest函数结束时删除对象时抛出异常:

System.AccessViolationException:尝试读取或写入受保护的内存.这通常表明其他内存已损坏

如果我将Current()函数的返回类型更改为:

T* Current()
{
   T* current = dynamic_cast<T*>(this);
   return current;
}
Run Code Online (Sandbox Code Playgroud)

然后一切都很好,单元测试结束时没有访问冲突.有人能告诉我第一个Current()实现有什么问题吗?谢谢,bouchaet.

Ada*_*wen 5

更改CreateSelectStatement后返回已实现函数的值(不是纯虚函数)

string CreateSelectStatement() const { return ""; }
Run Code Online (Sandbox Code Playgroud)

并更改阅读器的声明(您的声明应严格解释为C++中的函数原型)

SomeValueReader<A> reader;
Run Code Online (Sandbox Code Playgroud)

上面的例子使用gcc编译并执行时没有错误,这让我相信上面的源代码中可能没有实际的错误.不幸的是我现在无法用VC测试.

我看不出任何明显的原因,为什么你建议的更改会解决问题,我能看到的唯一其他错误是Base没有声明虚拟析构函数,这意味着如果你删除了一个Base*(或其他一些派生的)不是实际类型的类)不正确的析构函数将被触发.你应该声明它

virtual ~Base() {}
Run Code Online (Sandbox Code Playgroud)

即使它是空的.

在风格上,以这种方式使用模板和虚函数也有点奇怪,因为在这里你使用模板在编译时解析函数.我看不出为什么SomeValueReader需要从T派生(而不是有一个成员变量).