簡體   English   中英

如何創建一個行為幾乎與 int64_t 完全相同的類型安全句柄,但不允許句柄類型之間的隱式轉換?

[英]How can I create a typesafe handle that behaves almost exactly like int64_t, but disallow implicit conversions between handle types?

語境:

我創建了一個名為“ComponentManager”的通用容器。 它看起來大致是這樣的:

typedef uint64_t handle;

template<typename T>
class ComponentManager {
    std::vector<T> components; // stores the elements themselves
    std::unordered_map<handle, unsigned int> handles; // maps handles to indices
    HandleGenerator handleGenerator; // class that generates unique handles
public:
    size_t size() const;

    handle add(T component); // Adds an element to the component manager, and returns the handle for that element

    T* find(handle key) const; // returns a pointer to the element refered to by this handle, or NULL if none exists

    void remove(handle key); // may invalidate all iterators. Does NOT invalidate any key (except the one to the element deleted)

    ComponentManagerIterator<T> begin();
    ComponentManagerIterator<T> end();
};

此 class 使用 hash map 的句柄 - > 索引以允許對元素進行 O(1) 隨機訪問,同時還允許在元素內進行緩存有效迭代,因為它們的空間局部性沒有特定的順序向量刪除元素時可能會隨機播放)

問題:

就目前而言,程序中的所有句柄都具有相同的類型,因此用戶可能會錯誤地嘗試使用對應於一個句柄管理器的句柄來訪問完全不相關的句柄管理器中的元素。

我想通過根據句柄管理器的類型為句柄本身賦予不同的類型來緩解這種情況。 與此類似的東西:

template<typename T>
class handle
{
public:
    uint64_t key;
    explicit handle(uint64_t key_) : key(key_) {}
}

因此,如果您嘗試為錯誤類型的句柄管理器使用句柄,則代碼將無法在沒有顯式轉換的情況下編譯。

但是,問題在於我有時仍想將這些句柄視為整數。 I would like all of the regular integer operations to be defined for this type (comparison, bitwise ops, etc), and I would like algorithms specialized for integers (such as std::hash) to function as if my type was an integer. 有沒有辦法在不自己手動實現這些操作的情況下做到這一點?

如果有更好的方法以不同的方式獲得這樣的類型安全,我願意接受其他方式來實現這一點。

編輯:我還應該提到,在我的程序中,任何給定類型 T 也只有一個組件管理器,因此僅類型就足以識別特定的組件管理器。

編輯 2(附加上下文):我在賦予句柄唯一類型時看到的一個好處是,我可以重載單個 function 以根據句柄的類型訪問不同的組件管理器。

您可以創建一個方法來執行從handleuint64_t的隱式轉換。

template<typename T>
class handle
{
public:
    operator uint64_t() { return key_; }
    explicit handle(uint64_t key) : key_(key) {}
private:
    uint64_t key_;
}

當上下文需要它時,這將自動將handle<T>轉換為uint64_t

#include <iostream>
#include <string>

template<typename T>
class handle
{
public:
    operator uint64_t() { return key_; }
    explicit handle(uint64_t key) : key_(key) {}
private:
    uint64_t key_;
};

template<typename T>
int plus_20(T t)
{
    return t + 20;
}

int main()
{
  handle<int> hand(4);
  std::cout << hand << std::endl; // 4
  std::cout << hand + 1 << std::endl; // 5
  std::cout << (hand << 3) << std::endl; // 32
  std::cout << plus_20(hand) << std::endl; // 24
  //std::cout << plus_20<std::string>(hand) << std::endl; // doesn't compile

  std::unordered_map<uint64_t, std::string> umap;
  umap[hand] = "test";
  for(auto [key, value] : umap)
  {
      std::cout << key << " --> " << value << std::endl;
  }
}

現在,您的 class 可以如下所示(跳過未更改的部分):

template<typename T>
class ComponentManager {
    // ...
    std::unordered_map<uint64_t, unsigned int> handles; // maps handles to indices
    // ...
public:
    // ...
    handle<T> add(T component); // Adds an element to the component manager, and returns the handle for that element

    T* find(handle<T> key) const; // returns a pointer to the element refered to by this handle, or NULL if none exists

    void remove(handle<T> key); // may invalidate all iterators. Does NOT invalidate any key (except the one to the element deleted)
    // ...
};

請注意, ComponentManager class 中的std::unordered_mapuint64_t作為其鍵。 確保類型安全的是公共方法add()find()remove()的參數和返回值。 handle<T>構造函數上的explicit指定做了很多工作,以確保不能將一種句柄隱式轉換為另一種句柄。


handle類型進行完全黑盒化的附加功能:

如果要將unordered_map鍵保留為handle<T> ,則可以這樣做而無需定義所有必需的操作。 只需告訴unordered_map構造函數使用哪些:

template<typename T>
class ComponentManager {
    // ...
    std::unordered_map<handle<T>,
                       unsigned int,
                       std::hash<uint64_t>,
                       std::equal_to<uint64_t>> handles; // maps handles to indices
    // ...
};

如果您using key_type = uint64_t;添加公共handle<T> class 模板,這可以概括為

template<typename T>
class ComponentManager {
    // ...
    std::unordered_map<handle<T>,
                       unsigned int,
                       std::hash<typename handle<T>::key_type>,
                       std::equal_to<typename handle<T>::key_type>> handles; // maps handles to indices
    // ...
};

這允許更改handle<T> class 模板內的日期,而無需更新任何其他代碼。

暫無
暫無

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

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