![](/img/trans.png)
[英]Is static member variable initialized in a template class if the static menber is not used?
[英]Template class static string member not initialized correctly
我正在尝试围绕glib2.0
的GVariant
创建一个简单的包装器。
我想象有一个模板化的 class 将用于派生GVariant
类型的格式字符串:
template <typename T>
struct Object
{
static const std::string format_string;
};
现在我已经硬编码了基本整数类型和字符串,并确定了派生arrays和字典的方式如下:
// Integral types
template <>
const std::string Object<uint8_t>::format_string{"y"};
...
// String
template <>
const std::string Object<std::string>::format_string{"s"};
// Array
template <typename T>
struct Object<std::vector<T>>
{
static const std::string format_string;
};
template <typename T>
const std::string Object<std::vector<T>>::format_string{
"a" + Object<T>::format_string};
// Dictionary
template <typename K, typename V>
struct Object<std::map<K, V>>
{
static const std::string format_string;
};
template <typename K, typename V>
const std::string Object<std::map<K, V>>::format_string{
"{" + Object<K>::format_string + Object<V>::format_string + "}"};
对于元组,我使用以下字符串推导方法:
template <typename T, typename... Ts>
std::string derive_string()
{
if constexpr (sizeof...(Ts)) {
return Object<T>::format_string + derive_string<Ts...>();
} else {
return Object<T>::format_string;
}
}
// Tuple
template <typename... Ts>
struct Object<std::tuple<Ts...>>
{
static const std::string format_string;
};
template <typename... Ts>
const std::string Object<std::tuple<Ts...>>::format_string{
"(" + derive_string<Ts...>() + ")"};
但是,当我尝试调试打印每个Object
class 的format_string
成员时
using IntArray = std::vector<int>;
using IntStrMap = std::map<int, std::string>;
using Int_Double_Bool = std::tuple<int, double, bool>;
using Int_IntStrMap_Bool = std::tuple<int, IntStrMap, bool>;
using Int_IntDoubleMap = std::tuple<int, std::map<int, double>>;
std::cout << "bool type:\n " << Object<bool>::format_string
<< "\nuint8_t type:\n " << Object<uint8_t>::format_string
<< "\nint16_t type:\n " << Object<int16_t>::format_string
<< "\nuint16_t type:\n " << Object<uint16_t>::format_string
<< "\nint32_t type:\n " << Object<int32_t>::format_string
<< "\nuint32_t type:\n " << Object<uint32_t>::format_string
<< "\nint64_t type:\n " << Object<int64_t>::format_string
<< "\nuint64_t type:\n " << Object<uint64_t>::format_string
<< "\ndouble type:\n " << Object<double>::format_string
<< "\nstring type:\n " << Object<std::string>::format_string
<< "\n[int] type\n " << Object<IntArray>::format_string
<< "\n{int: str} type\n " << Object<IntStrMap>::format_string
<< "\n(int, double, bool) type\n "
<< Object<Int_Double_Bool>::format_string
<< "\n(int, {int: str}, bool) type\n "
<< Object<Int_IntStrMap_Bool>::format_string
<< "\n(int, {int: double}) type\n "
<< Object<Int_IntDoubleMap>::format_string;
<< std::endl;
我得到以下信息:
bool type:
b
uint8_t type:
y
int16_t type:
n
uint16_t type:
q
int32_t type:
i
uint32_t type:
u
int64_t type:
x
uint64_t type:
t
double type:
d
string type:
s
[int] type
ai
{int: str} type
{is}
(int, double, bool) type
(idb)
(int, {int: str}, bool) type
(i{is}b)
(int, {int: double}) type
(i)
从最后两个 object 打印输出可以看出,包含在别处使用的类型 ( {int: str}
) 的元组是正确派生的,而没有的元组 ( {int: double}
) 则不是。
我在这里做错了什么?
使用 C++17,您可以简单地执行以下操作:
template <typename... Ts>
const std::string Object<std::tuple<Ts...>>::format_string{
"(" + (Object<Ts>::format_string + ... + "") + ")"};
解决了 gcc Demo的问题
但是 clang 仍然存在问题(即使是std::vector<int>
)。
我怀疑Static Initialization Order Fiasco 。
使用函数而不是先前/缓存的结果适用于两个编译器:
std::string make_string(std::type_identity<bool>) { return "b"; }
std::string make_string(std::type_identity<uint8_t>) { return "y"; }
std::string make_string(std::type_identity<uint16_t>) { return "n"; }
std::string make_string(std::type_identity<int16_t>) { return "q"; }
std::string make_string(std::type_identity<int32_t>) { return "i"; }
std::string make_string(std::type_identity<uint32_t>) { return "u"; }
std::string make_string(std::type_identity<int64_t>) { return "x"; }
std::string make_string(std::type_identity<uint64_t>) { return "t"; }
std::string make_string(std::type_identity<double>) { return "d"; }
std::string make_string(std::type_identity<std::string>) { return "s"; }
template <typename T>
std::string make_string(std::type_identity<std::vector<T>>)
{
return "a" + make_string(std::type_identity<T>{});
}
template <typename K, typename V>
std::string make_string(std::type_identity<std::map<K, V>>)
{
return "{" + make_string(std::type_identity<K>{})
+ make_string(std::type_identity<V>{}) + "}";
}
template <typename... Ts>
std::string make_string(std::type_identity<std::tuple<Ts...>>)
{
return "(" + (make_string(std::type_identity<Ts>{}) + ... + "") + ")";
}
template <typename T>
struct Object
{
static const std::string format_string;
};
template <typename T>
const std::string Object<T>::format_string = make_string(std::type_identity<T>{});
这是因为您没有正确定义初始化format_string
变量的顺序。 下面举例说明原理。
...
extern string a;
string b1 = a + "b";
string a = "a";
string b2 = a + "b";
int main()
{
// At this point, b2 will be "ab", but b1 will be "b".
...
}
在您的问题中,很难定义顺序,因为有多种可能的类型组合。 所以对于此类问题使用static方法而不是static成员变量。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.