簡體   English   中英

模板類的容器對象

[英]Container Object of Templated Class

下面是實現的(非常)精簡代碼。

庫代碼如下:

#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類實現了某種惰性復制,因為在實際應用程序中存儲的對象相對較大。
  • 容器可用於任意數量的線程,而TemplatedBase類的惰性副本正在處理線程安全性。
  • 容器用於本身不知道(也不需要知道)派生類的上下文中。

雖然代碼本身可以正常工作,但我正在尋找一種不同的設計,該設計使得在讀出期間不需要dynamic_cast和類型規范。

我嘗試了幾種類型的類型擦除(boost :: type_erasure,boost :: variant)和不同的容器類型。 但是,我總是遇到object()函數的不同返回類型的問題。

如果要返回Base以外的特定類型,則無法避免將類型名稱放在此處:

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

編譯器無法知道在編譯時返回什么,因為直到運行時才知道該值。

至於您對演員表的重新詮釋,您不喜歡它是正確的。 這需要是dynamic_cast,或者您必須構建其他方式來告訴您的對象在運行時進入系統的類型。 另外,您可能意味着static_cast不重新解釋static_cast 但是,如果您不完全知道每次查詢都將正確的對應類型指定為與存儲對象的實際運行時類型匹配的模板參數,則static_cast也不正確。

現在,如果打錯電話,系統將導致未定義的行為。 您的代碼“運行正常”,因為您沒有使用不兼容的模板類​​型調用getObject,但是一旦這樣做,您的程序就可能無法正常運行。

讓我們考慮一下您到底在做什么:

  • 你在鑄造
    • 基類的指針指向其派生子類的指針,
    • 在非虛擬繼承鏈中,
    • 而不會拋棄const
  • 您沒有檢查結果的正確性。

因此, 此處要使用的正確轉換是static_cast

但是,如果用戶不小心用不匹配的類型調用container.getObject ,這將中斷(=導致未定義的行為)。 因此,您應該按照xaxxon的answer中的說明檢查轉換結果的正確性。

不,沒有避免避免在轉換中指定目標類型的方法。 必須為對象指定靜態類型才能使用它,因為ptr->object()的結果不會被類型擦除。 甚至boost::variant要求用戶在實際要訪問存儲的對象時(例如,在訪問者的簽名中)指定靜態類型。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM