【问题标题】:How to bypass template virtual function to attain my goal?如何绕过模板虚函数来实现我的目标?
【发布时间】:2011-07-02 08:23:32
【问题描述】:

我知道这不是合法的 C++,因为编译器无法确定 vtable 到底有多大。我正在寻找替代品。

基本上,我有一个抽象基类,它为一组派生类定义接口。通过此接口公开的一些函数的返回值由输入参数 sKeyName 确定。我的预期代码发布在下面。

class DataAccess
{ 
    public: 
        template <class T> 
        virtual const Array2D<T>* GetData(std::string sKeyName) const = 0;

        template <class T>  
        virtual Array2D<T>* GetData(std::string sKeyName) = 0; 
}; 

谁能给我一个解决方法来获得这个功能?任何帮助表示赞赏。提前谢谢你。

【问题讨论】:

  • 接缝你正在打破“告诉不问”的设计原则。您到底要归档什么?

标签: c++ templates function virtual


【解决方案1】:

您需要做的是定义一个单独的模板化界面。

class DataAccess {
    template<typename T> class InnerInteface {
        virtual Array2d<T>* GetData(std::string) = 0;
    };
};
class OHai : public DataAccess, DataAccess::InnerInterface<float> {
};
int main() {
    DataAccess* D = new OHai;
    if (DataAccess::InnerInterface<float>* data = 
       dynamic_cast<DataAccess::InnerInterface<float>>(D)) {
    }
}

可能为您提供所需的东西。您也可以模板化基类,但我猜这是不允许的。

【讨论】:

  • 为什么继承自DataAccess?看起来像一个空类;)
  • @VJo:因为这为所有数据访问器提供了一个公共基类。只从模板继承,没有通用的基类。
  • 您需要使DataAccess 具有多态性(例如,通过给它一个虚拟析构函数),dynamic_cast 才能工作。
  • @Mike:我也没有修复访问修饰符。重点是展示技术,而不是解决每一个小问题。
【解决方案2】:

你问的是动态函数,但你的主要问题不在那里,而是在这里:

通过该接口公开的部分函数的返回值由输入参数sKeyName决定。

C++ 是一种静态类型语言,这意味着您不能为依赖于参数值的函数提供返回类型。暂时忽略继承,您提供的代码要求用户确定返回的数组的类型独立传递的参数:

struct SimpleDataAccess {
   template <typename T>
   array2d<T>* get_data( std::string const & which ) {
      return new array2d<T>();
   }
};
int main() {
   SimpleDataAccess accessor;
   array2d<int> = accessor.get<int>( "int" ); // <int> at the place of call fixes
                                              // the return type, not "int" !
}

现在,如果您愿意接受这种情况(即调用者将知道并设置返回类型),则有不同的方法可以为您的语言不允许模板化虚拟函数的特定问题提供解决方法。想到的第一件事很好,因为它也遵循 NVI 习惯用法(并显示了它的重要性):为数据提供非虚拟公共模板化访问器,并根据固定返回类型的虚函数实现它。

class DataAccessor {
    virtual Type get_data_impl( std::string const & ) = 0;
public:
    template <typename T>
    array2d<T>* get_data( std::string const & which ) {
       Type tmp = get_data_impl( which );
       return convert( tmp );
    }
};

假设我们可以解决Typeconvert 是什么,我们就有了解决方案。这是 NVI 惯用语的一个很好的例子:用户提供的接口(公共的、非虚拟的)与扩展所需的接口(私有的、虚拟的)不同。这两个合约不同,您的用户要求您提供指向特定具体 array2d 实例化的指针,但该语言不允许您从扩展中要求相同的合约,但这不是问题,因为它们是不同的 接口。

现在回到Typeconvert。这两者是相关的,您可以采用不同的方法。最简单的实现是拥有一个array2d_base 类,所有array2d&lt;T&gt; 都从该类派生(通过提供一个启用RTTI 的虚拟析构函数):

struct array2d_base {
   virtual ~array2d_base() {}
};
template <typename T>
class array2d : public array2d_base {
   // implementation
};
// Type == array2d_base*
// convert == dynamic_cast< array2d<T>* >
template <typename T>
array2d<T>* DataAccessor::get_data( std::string const & s ) {
   return dynamic_cast< array2d<T>* >( get_data_impl( s ) );
}

如果您不能扩展或修改array2d 类,那么您可以通过类型擦除获得类似的结果。这将具有在array2d 中不需要RTTI 的优点,而仅在类型擦除支持中。最简单的此类实现是在内部接口中使用boost::any

// Type == boost::any
// convert == boost::any_cast< array2d<T>* >
template <typename T>
array2d<T>* DataAccessor::get_data( std::string const & s ) {
   boost::any tmp = get_data_impl(s);
   return boost::any_cast< array2d<T>* >( tmp );
}

【讨论】:

  • 发布问题后,突然的紧迫感迫使我离开了我的电脑。很抱歉所有回答我问题的人。
【解决方案3】:

我不确定这是否能回答您的问题,但您可以查看CRTP。然后你会这样做:

template< typename T >
struct B
{
  virtual Array2D<T>* GetData(std::string sKeyName) = 0; 
};

struct A : public B< int >
{
  virtual Array2D<int>* GetData(std::string sKeyName)
  {
    // implement
  }
};

【讨论】:

    【解决方案4】:
    • 模板函数不能是虚函数。
    • Array2D 需要一个模板参数。即使模板类的类型是默认的,也需要&lt;&gt;

    【讨论】:

      【解决方案5】:

      让所有可以从 GetData 返回的不同类型从基类继承并返回指向该类的指针。示例:

      class Data {};
      class Array2D: public Data{};
      class DataAccess
      { 
          public:
              virtual Data* GetData(std::string sKeyName) const = 0;
      };
      

      【讨论】:

        猜你喜欢
        • 2010-12-02
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2010-09-11
        • 2012-07-03
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多