Using PODs in C++11

In one of my previous posts I tried to show how Boost’s value_initialized can be used to make the usage of PODs (POD = “Plain Old Data”) in C++03 a bit easier. In C++11 this is not necessary. The new language standard provides a number of features that makes the usage of PODs even simpler (although not as simple as one could imagine). Let’s try to explore them.

A POD, at least for the purpose of this post, is a very simple class whose purpose is to aggregate a number of objects into one. No access protection, no fancy methods. (Note that the Standard offers a very strict definition of POD, which is somewhat different than what we discuss here.) We need it in order to reduce a number of function parameters to one, or make one return type where we need to return multiple values. This is similar to std::tuple, but gives access to aggregated objects by name rather than by index:

struct PixelByName
{
  int   x;
  int   y;
  Color color;
};

using PixelByIndex = std::tuple<int, int, Color>;

bool operator==(PixelByIndex byIndex, PixelByName byName)
{
  return get<0>(byIndex) == byName.x 
      && get<1>(byIndex) == byName.y
      && get<2>(byIndex) == byName.color;
}

Note that the C++ Standard also defines the term “POD”, but there it means something slightly different. (A POD struct is a non-union class that is both a trivial class and a standard-layout class, and has no non-static data members of type non-POD struct, non-POD union (or array of such types).)

You already know that you get a number of special member functions for free, generated by the compiler: destructor copy constructor, copy assignment, move constructor and move assignment. We will not mention them anymore and focus only on the missing ones.

Default value

First thing, we may observe, that thus defined class PixelByName is missing a predictable default value. But wait, are we really missing the default predictable default value? What is the good default value for our pixel? point (0, 0)? Always? What color? Black? White? Transparent? Do we even need our type to be default constructable? The answer to the last question is, we probably do: this is because many STL containers and algorithms require that ‘type T’ is default constructable. For instance map’s operator[]. This default value need not be very meaningful, because it is only used to be overwritten by subsequent operations. For instance:

Date d;
std::cin >> d;
use(d);

There is no need to initialize d to any predictable value because we will overwrite it in a moment. But it is essential that we have a default constructor in order for our valid example to compile. The default value need not be even predictable: it can be different each time we execute the code. And providing a predictable value might come at the cost of run-time efficiency (if you care for such efficiency). And such default constructor is also generated for POD’s — provided that you do not attempt to provide any other constructor.

If the above argument does not sound convincing enough, and you need to provide a predictable default value, you can provide the default member value, as suggested by Michal Mocny in the other post:

struct PixelByName
{
  int x = 0;
  int y = 0;
  Color color = black;
};

This is a new (in C++) way of specifying default values that your class’s members will be initialized with if you do not specify the initial value explicitly in the constructor’s initialization list. (By the letter of the Standard a class that uses this feature is not a POD any more; however, for the purpose of this post, we still call it a POD).

Constructing with value

Next missing feature of our class (at least in C++03) is the ability to create a new object by specifying value of each sub-object. Well, we can do it in C++03:

PixelByName pix = {2, 2, red};
setPixel(pix);

But we cannot do it in an expression (create a temporary) and are therefore forced to create an automatic object. This forces a copy constructor — if setPixel takes argument by value — rather than a move constructor and we need to explicitly call std::move. We can fix that by providing a constructor with parameter list matching our sub-object list. But in C++11 there is an alternative that does not require of us to define a constructor for a POD whose meaning is obvious enough. We can use the new list-initialization syntax:

setPixel(PixelByName{2, 2, red});
setPixel({2, 2, red});

Note that the type type of our temporary is not required if it can be unambiguously deduced from the function’s argument list.

In most cases, these are the only construction variants that you will need for PODs: default construction, copy, move and “member-wise value construction.” And for that, you do not have to define a single constructor yourself.

Comparison

Since using PODs goes so smoothly you may start to suspect, or expect, that more operations should be available out of the box. For instance, compiler should have no problem with searching PixelByName in an STL collection, because the meaning of operator== is obvious.

Well, you do not get operator== for free; and it is not entirely obvious how such equality comparison should be defined. You could argue that it should be a member-wise comparison of all members. In fact this is how tuples work:

using PixelByIndex = std::tuple<int, int, Color>;

bool compare(PixelByIndex a, PixelByIndex b)
{
  bool eq {a == b}; // equal all members
  bool ne {a != b}; // at least one member unequal
  bool lt {a <  b}; // lexicographical-less (all members)
}

Tuples can afford this default behavior of comparison, much like containers, because their members are deprived of any meaning: they are just elements in a (perhaps heterogeneous) collection. However, when you assign names to members you also give an interpretation of what they represent, and based on what they represent, member-wise comparison may not be the correct behavior. For instance, when you store objects of type PixelByName in std::map, should two pixels at the same position with a different color be treated as same or different? Or in general, some members of an object can be thought of as a “key” and others as a “value”; comparison (i.e., order and identity) should be only checked by “key” members — never by “value” members.

Also observe that if compiler was always generating member-wise comparison we would get an incorrect behavior if we wanted to implement a simple rational number class:

struct Rational
{
  int num, den;
};

Rational a{1, 2}, b{2, 4};
assert (a != b); // if auto generated!

You could argue that if any interpretation could be given to our class, it is not a POD, because it no longer only blindly groups sub-objects; and you would probably be correct. But the compiler cannot detect our intentions or our interpretations, and if it were to provide implementations for comparisons it would have to do them for either case, also for those cases where we have some interpretation for our type.

For the above reasons, the plans for the next revision of the C++ Standard, as indicated in N2346, are to provide an option for the programmer to explicitly request of the compiler to generate the comparison operator with the ‘default’ semantics. Such request would probably be spelled something like this:

bool operator==(PixelByName const& a, PixelByName const& b) = default;

Sequential access to POD’s sub-objects

We have seen in the beginning that std::tuple offers a similar functionality as POD. In some ways tuples are superior: you do not have to spend time defining them — the template is instantiated “on the fly” where you need it. They also offer comparison operations and more: sequential (by index) access to their members; thus, we can write code that iterates over members of a tuple (e.g., if we want to automatically implement archiving functions).

On the other hand, tuples come with one obvious disadvantage: members thus contained do not have meaningful names, and in cases where you keep a number of them, especially of the same type, you can easily confuse them:

bool sameLine(PixelByIndex p, PixelByIndex q)
{
  return get<0>(p) == get<0>(q);
}

In the above example, it is difficult to assert whether the implementation is correct or not. Ideal situation would be to have both sequential access and access by name to sub-objects of a POD. Clearly, you cannot do that in C++ for free. But let’s try to see what minimum work we have to do to achieve a similar effect. If you can afford using Boost, Fusion library comes with tools for adapting your structure.

# include <boost/fusion/adapted/struct/adapt_struct.hpp>

BOOST_FUSION_ADAPT_STRUCT
(   
  /* MyNamespace:: */ PixelByName,
  (int, x)(int, y)(Color, color)
)

Having thus taught the library to use our type PixelByName, we can access its sub-objects by index:

# include <cassert>
# include <boost/fusion/sequence.hpp>

int main()
{
  PixelByName p = { 10, 110, {0xFFFFFF00} };
  assert (boost::fusion::at_c<0>(p) == p.x);
  assert (boost::fusion::at_c<1>(p) == p.y);
  assert (boost::fusion::at_c<2>(p) == p.color);
}

But using this clever macro rises some discomfort: we need to learn some non-trivial domain-specific syntax of the macro, and we still have to provide the definition of our POD twice. While the library provides another tool, BOOST_FUSION_DEFINE_STRUCT, for defining the struct as well as its sequential access only once, this solution is not perfect either. Now the syntax for defining a POD is just arcane: it will confuse not only programmers, but also IDEs. And, since the macro isn’t perfect, thus defined struct is not an aggregate and we do not have a “value” constructor.

There is a proposal in the C++ Committee to add the ability for the programmers to obtain a sequential access to any user-defined class: N3326. However, given the tight schedule of the committee it is unlikely that it is added in the next standard revision.

This entry was posted in programming and tagged , , . Bookmark the permalink.

12 Responses to Using PODs in C++11

  1. Lukasz Mendakiewicz says:

    A simple yet effective way to get around tuple indexing confusion is to use good ol’ enumerations,

    get<pixel::x>(byIndex) == byName.x
        && get<pixel::y>(byIndex) == byName.y
        && get<pixel::color>(byIndex) == byName.color;
    

    That’s slightly better, alas without any type safety.

  2. Nick Krempel says:

    The link to N3326 near the end is incorreclty a duplicate of the previous Boost link.

  3. So this is pretty interesting Andrzej, but it doesn’t demonstrate how `value_initialized` becomes unnecessary. If I have a generic `T` and I want it value-initialized, how do I do it in C++11?

  4. Hi Dave. You are right, this post doesn’t demonstrate that. But are you saying that this claim (`value_initialized` becomes unnecessary) is untrue, or are you just encouraging me to demonstrate it?

    To answer your question, if I understood it correctly, in case of an automatic variable, you would probably write:

    template <typename T>
    T fun()
    {
      T ans{};
      // ...
      return ans;
    }
    

    In case of class member, you would use a similar notation:

    template <typename T>
    class C
    {
      T mem{};
      // ...
    };
    
  5. Paul Keir says:

    Was there any progress on N3326 for C++14? I do like the sound of it.

  6. N3326 was not in focus for C++14. Likely, it will be superseded by some other, more general-purpose reflection mechanism, as indicated in N3403. But this is no near future, I guess.

  7. magras says:

    > struct PixelByName
    > {
    > int x = 0;
    > int y = 0;
    > Color color = black;
    > };

    This struct isn’t trivial class according to C++11 (N3242). Therefore it isn’t POD too.
    See 9 §10, 9 §6, 12.1 §5.

    • That’s right. But I think I mention at the beginning that I what I call ‘POD’ is not the same as the POD from STD. I added more such remarks now, so this should clear the confusion. Thanks.

  8. P Pan says:

    Did I miss something in the article? Just want to know WHY we care if something is a POD or not?

Leave a comment

This site uses Akismet to reduce spam. Learn how your comment data is processed.