Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Is it safe to rely on static object adresses?

Tags:

c++

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) ?

like image 610
Drax Avatar asked Dec 16 '22 02:12

Drax


2 Answers

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.

like image 176
Mike Seymour Avatar answered Dec 27 '22 12:12

Mike Seymour


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).

like image 42
Joseph Mansfield Avatar answered Dec 27 '22 11:12

Joseph Mansfield