Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

C++11: Does an assignment operator prevent a type from being POD, and thus being global-initialized?

Background: I'm in a large-code environment where the undefined order in which global constructors are run is problematic. So I have a custom class that is designed to delay initialization until first-use. All its magic occurs inside its operator* and operator-> functions; they're the only thing defined. It also stores some state within itself, to make available to the automatic-initialization function. That state must, of course, be POD, so that the whole class is POD, so that it can be completely set up before anyone's code starts running, so that all code everywhere can use all globals everywhere, without fear that the globals haven't been set up yet.

A while back someone added a private, never-defined assignment operator, so that the type would never be assigned to (it's not designed to ever change anyway). Now someone else is saying the class is broken because it's not POD. If instead of it being declared-but-not-defined, I declare it as "=delete", I'm thinking that's somehow better. And indeed, with that change, std::is_pod<>::value returns true for the type.

But does as assignment operator prevent a type from being POD? I thought the requirements were just that it had to have only public data members, no virtual methods, and no constructor or destructor.

And more to the point for my situation: does the presence of a never-defined assignment operator prevent the class from being able to be initialized at global initialization time, along with all the other global PODs?

Reduced example:

struct LazyString {
  const char *c_str;

  bool has_been_inited;
  string *lazy_str_do_not_use_directly;

  string &operator*() { return *get(); }
  string *operator->() { return get(); }

 private:
  string *get() {
    // The real code uses a mutex, of course, to be thread-safe.
    if (!has_been_inited) {
      lazy_str_do_not_use_directly = new string(c_str);
      has_been_inited = true;
    }
    return lazy_str_do_not_use_directly;
  }

  // Does this make the class non-POD?
  // If so, does that mean that global variables of this type
  // will not be initialized at global-initialization time, that wonderful
  // moment in time where no code has yet been run?
  void operator=(const LazyString&);

  // If I do this instead, it breaks C++03 compatibility, but is this somehow better?
  void operator=(const LazyString&) = delete;
};

LazyString lazy = { "lazy" };

int main(int argc, char *argv[]) {
  std::cout << *lazy;
}
like image 501
jorgbrown Avatar asked Sep 25 '14 17:09

jorgbrown


2 Answers

Does an assignment operator prevent a type from being POD

Yes. A POD type must be trivial; and so must be trivially copyable; and so must have no non-trivial copy or move assignment operators.

Any user-provided operator is non-trivial, so declaring the copy constructor makes the class non-trivial, and hence non-POD.

and thus being global-initialized?

No. Any instantiable type, POD or not, can be a global variable.

UPDATE: From the comment, you meant to ask:

Can it be statically, rather than dynamically, initialised?

Yes, since it has a trivial constructor; as long as the initialiser is a constant expression. In your example, { "lazy" } is a constant expression, so LazyString can be statically initialised.

The important feature here is that it has a trivial constructor, not that it's POD. POD means it also meets various other requirements, not relevant to initialisation.

like image 86
Mike Seymour Avatar answered Sep 20 '22 12:09

Mike Seymour


C++ has several stages of initialization for non-local variables with static storage duration (global variable fall into this category) - regardless of whether the type is a POD or not.

  • zero initialization takes place before any other initialization
  • constant initialization takes place
  • dynamic initialization

The first two types of initialization must take place before dynamic initialization. Dynamic initialization is the situation where the order of initialization can be difficult to set. Some dynamic initialization is unordered, some is ordered within a translation unit, etc.

Even if you global variable is not POD, you can be sure that zero initialization will have taken place before any dynamic init.

See C++11 3.6.2 "Initialization of non-local variables" for details.

like image 33
Michael Burr Avatar answered Sep 22 '22 12:09

Michael Burr