H. Doebler
H. Doebler

Reputation: 651

Efficient non-trivial initialization of multiple members in C++ initialization lists

Suppose I want to write a class that represents an immutable normalized (norm == 1) vector in two dimensions along with a mutable int:

#include <cmath>
#include <iostream>

double norm2d(double x, double y)
{
  return sqrt(x*x + y*y);
}

class NormalizedVector {
public:
  NormalizedVector(double x, double y, int some_int)
    : m_x(x / norm2d(x, y)), m_y(y / norm2d(x, y)), m_some_int(some_int){};
  void set_some(int i) { m_some_int = i; }
private:
  const double m_x;
  const double m_y;
  int m_some_int;
  friend std::ostream& operator<< (std::ostream& os, const NormalizedVector& v) {
    return os << "(" << v.m_x << "," << v.m_y << ")" << " : " << v.m_some_int;
  }
};

int
main() {
  NormalizedVector v { 1, 1 , 42};
  std::cout << v << std::endl;
  v.set_some(23);
  std::cout << v << std::endl;
  return 0;
}

Is it in this situation possible to avoid calling norm2d(x, y) twice in the ctor while still using initialization lists? Note that default-constructing members and assigning them in the ctor's body is not possible because the vector components are const.

I am well aware that this MWE is not very realistic but in the past I have for several times stumbled upon a situation where one function (or "some code") was required to compute values for multiple members in the initialization list and where in-body-assigning them was not an option because the corresponding members where either const or not zero-cost-default-constructable.

Upvotes: 2

Views: 60

Answers (1)

Caleth
Caleth

Reputation: 62939

Yes, you can have a private constructor that takes an additional parameter, and delegate to that.

class NormalizedVector {
public:
  NormalizedVector(double x, double y, int some_int)
    : NormalizedVector(x, y, some_int, norm2d(x, y)) {};
  void set_some(int i) { m_some_int = i; }
private:
  NormalizedVector(double x, double y, int some_int, double norm)
    : m_x(x / norm), m_y(y / norm), m_some_int(some_int){};
  const double m_x;
  const double m_y;
  int m_some_int;
  friend std::ostream& operator<< (std::ostream& os, const NormalizedVector& v) {
    return os << "(" << v.m_x << "," << v.m_y << ")" << " : " << v.m_some_int;
  }
};

Upvotes: 3

Related Questions