2020-02-10

Little surprises #1

Systems that are complex can have surprises, and so can those that are subtle. So it is no surprise that systems that are both complex and subtle will have surprises.

Consider this code sample

In c++11.
#include <cstdlib>
#include <map>

class NonTrivialClass
{
public:
  NonTrivialClass() : m_a() {}

  // As it stand this code won't compile.
  //
  // Which of the following lines can be added (individually) to the class to
  // make it compile?
  //
  // NonTrivialClass(int a) : m_a(a) {}                              // (A)
  // explicit NonTrivialClass(int a) : m_a(a) {}                     // (B)
  // NonTrivialClass(const NonTrivialClass &) = default;             // (C)
  // NonTrivialClass & operator=(const NonTrivialClass &) = default; // (D)
  // NonTrivialClass & operator=(int a) { m_a = a; }                 // (E) 
  
private:
  int m_a;
};

int main(void)
{
  std::map<int, NonTrivialClass> m;

  m[42] = 1; // <== Error here

  return EXIT_SUCCESS;
}
The situation abstracted here into a minimal code sample actually came up for me today.

Answer

Lines (A) and (E) will do it.
  • The difference between the first two tells you why (A) works: it is implicitly constructing an object.
  • (C) and (D) are distracters. They don’t do anything in this case and there is no particular reason to expect that they should.
  • The reason that (E) works is complex (or at least multi-stepped). The value 42 is not currently a key to the map, so the compiler can default construct a new object, and return a reference to it. With that reference in hand NonTrivialClass::operator=(int) can be applied.
In the case of the code I was looking at this afternoon, the header was lengthy enough that while I was staring at the constructors trying to figure out what was going on the declaration of the assignment operators was off the screen.
Written with StackEdit.

No comments:

Post a Comment