[英]std::set used as a static templated member variable
我試圖制作類似Java風格的枚舉,我稱之為旗幟。 要求是每個標志都是靜態的,因此標志可以直接引用,每個標志存儲其名稱的字符串,整個集合可迭代並有助於查找。
我正在使用模板化,以便每組標志分開存儲(從而使我不必在每個子類中明確地放置一個集合)。
我確信這是一個啟動問題,因為運行程序的成功或失敗取決於包含標志聲明的目標文件的文件名(Ao segfaults但Zo運行正常。)
問題似乎是靜態初始化順序之一,這個代碼編譯完全正常但是當它運行時,gdb產生以下內容:
Program received signal SIGSEGV, Segmentation fault.
0x00007ffff751e0fa in std::_Rb_tree_decrement(std::_Rb_tree_node_base*) ()
from /usr/lib/gcc/x86_64-pc-linux-gnu/4.4.5/libstdc++.so.6
(gdb) bt
#0 0x00007ffff751e0fa in std::_Rb_tree_decrement(std::_Rb_tree_node_base*) ()
from /usr/lib/gcc/x86_64-pc-linux-gnu/4.4.5/libstdc++.so.6
#1 0x0000000000462669 in operator-- ()
at /usr/lib/gcc/x86_64-pc-linux-gnu/4.4.5/include/g++-v4/bits/stl_tree.h:199
#2 _M_insert_unique ()
at /usr/lib/gcc/x86_64-pc-linux-gnu/4.4.5/include/g++-v4/bits/stl_tree.h:1179
#3 insert () at /usr/lib/gcc/x86_64-pc-linux-gnu/4.4.5/include/g++-v4/bits/stl_set.h:411
#4 Flag () at include/../util/include/Flag.hpp:34
#5 ItemFlag () at include/Item.hpp:22
#6 __static_initialization_and_destruction_0 () at Item.cpp:15
#7 global constructors keyed to _ZN3code8ItemFlag5brickE() () at Item.cpp:86
#8 0x000000000046ac62 in ?? ()
#9 0x00007fffffffddc0 in ?? ()
#10 0x000000000046abb0 in ?? ()
#11 0x0000000000692c0a in ?? ()
#12 0x0000000000407693 in _init ()
#13 0x00007ffff7dded08 in ?? () from /usr/lib64/libboost_serialization-1_42.so.1.42.0
#14 0x000000000046abe7 in __libc_csu_init ()
#15 0x00007ffff6cd9b50 in __libc_start_main () from /lib64/libc.so.6
#16 0x0000000000408329 in _start ()
我的代碼如下:
template <class FlagType> class Flag
{
public:
Flag(int ordinal, String name):
ordinal(ordinal),
name(name)
{
flagSet.insert(this);
}
inline bool operator==(const Flag<FlagType>& e) const
{
//edited due to comment
//if(this->ordinal == e.getOrdinal()) return true;
//else return false;
return (this->ordinal == e.getOrdinal());
}
inline bool operator!=(const Flag<FlagType>& e) const
{
return !(*this==e);
}
static const std::set<const Flag<FlagType>*>& flagValues()
{
return flagSet;
}
const String& toString() const
{
return name;
}
const size_t& getOrdinal() const
{
return ordinal;
}
static int size()
{
return flagSet.size();
}
static const Flag<FlagType>& valueOf(const String& string)
{
typename std::set<const Flag<FlagType>*>::const_iterator i;
for(i = flagSet.begin(); i != flagSet.end(); i++)
{
if((**i).toString().startsWith(string))
{
return **i;
}
}
throw NotAFlagException();
}
protected:
static std::set<const Flag<FlagType>*> flagSet;
size_t ordinal;
String name;
private:
//added in response to comment to prevent copy and assignment, not compile tested
Flag<FlagType>(const Flag<FlagType>&);
Flag<FlagType>& operator=(const Flag<FlagType>&);
};
template <class FlagType> std::set<const Flag<FlagType>*> Flag<FlagType>::flagSet; //template
Item.hpp
class ItemFlag: public Flag<ItemFlag>
{
public:
static const ItemFlag brick;
private:
ItemFlag(int ordinal, String name):
Flag<ItemFlag>(ordinal, name){}
};
Item.cpp
const ItemFlag ItemFlag::brick(1, "brick");
我的第一篇文章,如果我的格式錯誤或者非特定,請告訴我。 PS。 奇怪的是,用向量替換set會產生一個工作程序,好像該集合在插入指針時遇到了麻煩。 為了測試這個,我用一組int替換了set,並嘗試在類初始化時插入0,這也導致了同樣的錯誤。
它很容易成為初始化問題的順序。 你基本上需要對集合使用某種延遲初始化,例如
static std::set<Flag<FlagType> const*>& flagSet()
{
static std::set<Flag<FlagType> const*> theOneAndOnly;
return theOneAndOnly;
}
而不是你的靜態變量。
雖然我在這:它可能不是一個很好的模板使用。 一個更好的解決方案是從具有更簡單格式的文件生成代碼,類似於:
[EnumName]
constant_name_1
constant_name_2
它可能需要不超過10行AWK,Perl或Python(根據您的口味)來解析它並輸出C ++頭文件和C ++源文件。 然后,您只需要維護簡單的格式。
如果類的構造函數將項插入到靜態集中,那么它的析構函數應該刪除它們。 您可能還需要一個復制構造函數和賦值運算符。 aso,在風格上,受保護的數據通常被認為是一件壞事。
不保證不同翻譯單元之間的靜態初始化順序。 從轉儲看起來似乎是在構造集合之前創建了ItemFlag
。 這使插入失敗。
更改文件名稱可能會影響鏈接過程中文件的順序。 這可以解釋為什么以A開頭的文件會在早期鏈接。
使其正常工作的唯一方法是在同一.cpp文件中定義set和ItemFlag
。 然后訂單總是從上到下。 如果它們位於不同的文件中,則鏈接器決定順序(主要是隨機的)。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.