模板类的容器对象

时间:2016-10-18 12:39:16

标签: c++ templates

下面是(非常)剥离的实现代码。

库代码如下:

#pragma once
#include <iostream>
#include <map>
#include <string>

// Necessary as interface and due to QObject Macro.
class Base
{
  public:
    Base( const std::string& name ) : name_( name )
    {
    }

    virtual ~Base(){}

    const std::string& name()
    {
        return name_;
    }

  private:
    std::string name_;
};

template < typename Derived, typename ObjectType >
class TemplatedBase : public Base
{
  public:
    TemplatedBase( const std::string& name ) : Base( name )
    {
    }

    ObjectType object()
    {
        return object_;
    }

    ObjectType object_;
};

class DerivedA : public TemplatedBase< DerivedA, int >
{
  public:
    DerivedA( const std::string& name ) : TemplatedBase< DerivedA, int >( name )
    {
    }
};

class DerivedB : public TemplatedBase< DerivedB, float >
{
  public:
    DerivedB( const std::string& name ) : TemplatedBase< DerivedB, float >( name )
    {
    }
};

class Container
{
  public:
    template < typename T >
    void addToMap( T& map_object )
    {
        const std::string name = map_object.name();
        // ASSERT( map_.find( name ) == map_.end() );
        map_.emplace( std::make_pair( name, &map_object ) );
    }

    template < typename T >
    auto getObject( std::string name ) -> decltype( ( ( T* )nullptr )->object() )
    {
        auto search = map_.find( name );
        // How can this dynamic_cast be avoided? 
        T* ptr      = dynamic_cast< T* >( search->second );
        // ASSERT( ptr == nullptr );
        return ptr->object();
    }

    std::map< std::string, Base* > map_;
};

使用示例:

int main( int argc, char* argv[] )
{
    Container container;

    DerivedA a( "Name_A" );
    DerivedB b( "Name_B" );

    container.addToMap( a );
    container.addToMap( b );

    // How can I avoid to specify the type in the map as template?
    auto object_a = container.getObject< DerivedA >( "Name_A" );
    auto object_b = container.getObject< DerivedB >( "Name_B" );
}

代码的一些解释:

  • 基类是必需的,因为Q_OBJECT宏需要非模板类以及接口。
  • 模板化的Base类实现了某种惰性副本,因为在实际应用程序中存储的对象相对较大。
  • Container可用于任意数量的线程,而TemplatedBase Class的惰性副本正在处理线程安全性。
  • Container用于不了解(也不需要知道)Derived Classes的上下文中。

虽然代码本身工作正常,但我正在寻找一种不同的设计,使得在读出期间不需要dynamic_cast和类型规范。

我尝试了几种类型的类型擦除(boost :: type_erasure,boost :: variant)和不同的容器类型。但是我总是遇到object()函数的不同返回类型的问题。

2 个答案:

答案 0 :(得分:2)

如果您想要返回Base以外的特定类型,则无法避免在此处输入类型名称:

    auto object_a = container.getObject< DerivedA >( "Name_A" );

编译器无法知道在编译时返回什么,因为直到运行时才知道该值。

至于你的重新演绎,你是不是喜欢它。这需要是dynamic_cast,或者您必须构建一些其他方法来告诉您的对象在运行时对系统的类型。此外,您可能意味着static_cast没有重新解释演员表。但是,如果您不完全知道每个查找都将指定正确的对应类型作为与存储对象的实际运行时类型匹配的模板参数,则static_cast也不正确。

现在,如果您拨打了错误电话,系统将导致未定义的行为。你的代码是&#34;工作正常&#34;因为您没有使用不兼容的模板类​​型调用getObject,但是一旦执行,您的程序可能无法正常工作。

答案 1 :(得分:1)

让我们考虑一下你在这里做了什么:

  • 你在施展
    • 基类的指针,指向其派生子类的指针
    • 在非虚拟继承链中,
    • 没有丢弃const - ness。
  • 您没有检查结果的正确性。

因此,此处使用的正确广播<{3}}

但是,如果用户意外调用类型不匹配的container.getObject,则会中断(导致未定义的行为)。因此,检查投射结果的正确性,如static_cast中所述。

不,没有办法避免在演员表中指定目标类型。您必须为对象指定静态类型才能使用它,因为ptr->object()的结果未被类型擦除。即使boost::variant要求用户在实际想要访问存储对象时(例如在访问者的签名中)指定静态类型。