Let's say i have this code :
#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;
}
The main question is:
Am i guaranteed that at the point of entering the main
function the references contained within the reference_wrapper
used as keys in test::ACTIONS_PROCESSORS_MAP_
will be corectly initialized to valid references to test::FIRST_KEY_
and test::SECOND_KEY_
respectivly, independently of the static order initialization ?
This question could be more generally summed up as :
Are pointers/references to satatic objects valid even before these objects initialization (i.e can the address change at some point) ?
Yes, you can safely take the address of an object once its storage has been allocated, even if it has not yet been initialised. The storage for static objects lasts for the duration of the program, so you can take the address at any time.
Of course, accessing the object itself outside its lifetime is not allowed.
It's worth mentioning that the initialisation of non-local static variables (that aren't in class template data members) in a single translation unit is ordered (§3.6.2/2):
Definitions of explicitly specialized class template static data members [... not this]. Other class template static data members [... not this]. Other non-local variables with static storage duration have ordered initialization. Variables with ordered initialization defined within a single translation unit shall be initialized in the order of their definitions in the translation unit.
So you know that FIRST_KEY_
will be initialised before SECOND_KEY_
will be initialised before ACTIONS_PROCESSORS_MAP_
.
But even if this weren't the case, it would still be okay. Since std::string
has non-trivial construction, there is some time between its storage has been allocated and the beginning of its lifetime. In this time it is okay to have a pointer to the object, as long as you don't do certain things with it. Your code doesn't do anything with it but store the pointer - you're safe. §3.8/5:
Before the lifetime of an object has started but after the storage which the object will occupy has been allocated or, after the lifetime of an object has ended and before the storage which the object occupied is reused or released, any pointer that refers to the storage location where the object will be or was located may be used but only in limited ways.
The limited ways are basically anything that doesn't involve accessing the object. In fact, it's fine to even perform indirection through the pointer as long as it doesn't end up undergoing lvalue-to-rvalue conversion (which essentially represents reading the object from memory).
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With