假设我有这段代码:
#include <iostream>
#include <unordered_map>
#include <functional>
using const_string_ref = std::reference_wrapper<const std::string>;
namespace std
{
template<>
struct hash<const_string_ref>
{
size_t operator()(const const_string_ref& ref) const
{
return std::hash<std::string>()(ref);
}
};
bool operator==(const const_string_ref& lhs,
const const_string_ref& rhs)
{
return (lhs.get() == rhs.get());
}
}
class test
{
public:
void process(const std::string& action)
{
(this->*(ACTIONS_PROCESSORS_MAP_.at(action)))();
}
private:
using action_processor = void (test::*)();
using actions_map = std::unordered_map<const_string_ref, action_processor>;
private:
static const std::string FIRST_KEY_;
static const std::string SECOND_KEY_;
static const actions_map ACTIONS_PROCESSORS_MAP_;
private:
void first_action()
{
std::cout << "first works" << std::endl;
}
void second_action()
{
std::cout << "second works" << std::endl;
}
};
const std::string test::FIRST_KEY_ = "first";
const std::string test::SECOND_KEY_ = "second";
const test::actions_map test::ACTIONS_PROCESSORS_MAP_ =
{{std::cref(FIRST_KEY_), &test::first_action},
{std::cref(SECOND_KEY_), &test::second_action}};
int main()
{
test t;
t.process("first");
t.process("second");
return 0;
}
主要问题是:
我保证在输入main
函数时,reference_wrapper
中用作test::ACTIONS_PROCESSORS_MAP_
中的test::FIRST_KEY_
所包含的引用将被核心初始化为对test::SECOND_KEY_
的有效引用和{{1}}分别独立于静态顺序初始化?
这个问题可以概括为:
即使在这些对象初始化之前,对satatic对象的指针/引用是否有效(即地址是否会在某些时候发生变化)?
答案 0 :(得分:7)
是的,即使尚未初始化对象,也可以在分配了对象后安全地获取对象的地址。静态对象的存储持续到程序的持续时间,因此您可以随时获取地址。
当然,不允许在其生命周期之外访问对象本身。
答案 1 :(得分:5)
值得一提的是,单个翻译单元中非局部静态变量(不在类模板数据成员中)的初始化是有序的(§3.6.2/ 2):
明确专门化的类模板静态数据成员的定义[...不是这个]。其他类模板静态数据成员[...不是这个]。具有静态存储持续时间的其他非局部变量具有有序初始化。在单个翻译单元中定义的具有有序初始化的变量应按其在翻译单元中的定义顺序进行初始化。
因此,您知道FIRST_KEY_
将在SECOND_KEY_
之前初始化ACTIONS_PROCESSORS_MAP_
之前进行初始化。
但即使不是这样,它仍然可以。由于std::string
具有非平凡的结构,因此在其存储分配和生命周期开始之间有一段时间。在这个时候,只要你不用它做某些事情,就可以有一个指向对象的指针。你的代码不会对它做任何事情但是存储指针 - 你是安全的。 §3.8/ 5:
在对象的生命周期开始之前但在对象占用的存储空间已经被分配之后,或者在对象的生命周期结束之后以及在重用或释放对象占用的存储之前,任何引用的指针可以使用对象所在或存在的存储位置,但只能以有限的方式使用。
有限的方式基本上是任何不涉及访问对象的方式。实际上,只要它不会最终进行左值到右值的转换(它实际上代表从内存中读取对象),甚至可以通过指针执行间接操作。