如何绕过模板虚函数来达到我的目标?

发布于 2024-11-18 04:32:57 字数 460 浏览 2 评论 0原文

我知道这不是合法的 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; 
}; 

有人可以给我一个解决方法来获得此功能吗?任何帮助表示赞赏。先感谢您。

I know this isn't legal C++ due to the compiler not being able to determine how big exactly the vtable is. I'm looking for alternatives.

Basically, I have an abstract base class defining the interface for a set of derived classes. The return values of some of the functions being exposed through this interface are determined by the input parameter sKeyName. My intended code is posted below.

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; 
}; 

Can somebody give me a workaround to get this functionality? Any help is appreciated. Thank you in advance.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(5

假装不在乎 2024-11-25 04:32:57

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

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)) {
    }
}

可能可以满足您的需求。您还可以模板化基类,但我猜这是不允许的。

What you'd need to do is define a separate, templated interface.

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)) {
    }
}

This might get you what you need. You can also template the base class, but I'm guessing that's not allowed.

双手揣兜 2024-11-25 04:32:57

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

通过该接口公开的一些函数的返回值由输入参数 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 实例化的指针,但该语言不允许您从扩展中要求相同的合约,但这不是问题,因为它们是不同的接口。

现在回到类型转换。这两者是相关的,您可以遵循不同的方法。最简单的实现是拥有一个 array2d_base 类,所有 array2d 都从中派生(通过提供虚拟析构函数来启用 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 );
}

You are asking about dynamic functions, but your main problem is not there, but rather here:

The return values of some of the functions being exposed through this interface are determined by the input parameter sKeyName.

C++ is a statically typed language, and that means that you cannot have a return type for a function that is dependent on the value of the arguments. Ignoring for the time being inheritance, the code that you presented requires the user to determine the type of the array that is being returned independently of the arguments passed:

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" !
}

Now, if you are willing to live with that (i.e. the caller will know and set the return type) there are different ways of providing workarounds for your particular problem of the language not allowing templated virtual functions. The first thing that comes to mind is nice in that it also follows the NVI idiom (and shows its importance): Provide a non-virtual public templated accessor to the data, and implement it in terms of a fixed return type virtual function.

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 );
    }
};

Assuming that we can resolve what Type and convert are, we have a solution. This is a good example of the NVI idiom: the interface offered by users (public, non virtual) differs from the interface required to extensions (private, virtual). The two contracts differ, your users require you to provide a pointer to specific concrete array2d instantiation, but the language does not allow you to require that same contract from your extensions, but that is not a problem because they are different interfaces.

Now back to Type and convert. These two are related, and there are different approaches that you can follow. The simplest to implement would be having an array2d_base class from which all array2d<T> derive (by providing a virtual destructor you enable 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 ) );
}

If you cannot extend or modify the array2d class, then you can achieve a similar result by means of type erasure. This will have the advantage of not requiring RTTI in array2d but only in the type erasing support. The simplest such implementation would be using boost::any in the inner interface:

// 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 );
}
我早已燃尽 2024-11-25 04:32:57

我不确定这是否回答了您的问题,但您可以查看 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
  }
};

I am not sure if this answers your question, but you can take a look into CRTP. Then you would do :

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
  }
};
缺⑴份安定 2024-11-25 04:32:57
  • 模板函数不能是虚拟的。
  • Array2D 需要一个模板参数。即使模板类的类型是默认的,也需要<>
  • Template functions cannot be virtual.
  • Array2D needs a template parameter. Even if type of template class is default, <> is needed.
埖埖迣鎅 2024-11-25 04:32:57

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

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

Let all your different types, that can be returned from GetData, inherit from a base class and return a pointer to that class. Example:

class Data {};
class Array2D: public Data{};
class DataAccess
{ 
    public:
        virtual Data* GetData(std::string sKeyName) const = 0;
};
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文