Copy constructor and implicit type conversion

#include <iostream>

class Complex
{
private:
  const double re;
  const double im;
public:
  Complex(double r = 0.0, double i = 0.0)
    : re(r), im(i)
  {
    std::cout << "constructor: " << *this << std::endl;
  }
  Complex(const Complex& c)
    : re(c.re), im(c.im)
  {
    std::cout << "copy constructor: " << *this << std::endl;
  }

  ~Complex()
  {}

  Complex operator=(const Complex& c) // field が immutable だと、デフォルトの代入はできない
  {
    return Complex(c);
  }

  friend Complex operator+(const Complex&, const Complex&);
  friend std::ostream& operator<<(std::ostream& s, const Complex&);
};
Complex operator+(const Complex& c1, const Complex& c2) // const .. & がないと
{                               // copy constructor が呼ばれる
  return Complex(c1.re + c2.re, c1.im + c2.im);
}
std::ostream& operator<<(std::ostream& s, const Complex& c)
{
  return s << "(" << c.re << ", " << c.im << ")@" << &c;
}

template<class T> T add(const T& a, const T& b)
{
  return a + b;
}

int main()
{
  double x = add(10, 20);
  std::cout << x << std::endl;

  Complex c1(10.0, -10.0);
  Complex c2(-2);

  std::cout << "add<>()" << std::endl;
  Complex c3(add(c1, c2));     // same to add<Complex>(c1, c2)
  std::cout << c3 << std::endl << std::endl;
 
  std::cout << "operator+()" << std::endl;
  Complex c4(c1 + c3);
  std::cout << c4 << std::endl << std::endl;

  std::cout << "operator+(), substitution" << std::endl;
  Complex c5 = c2 + c4;
  Complex c6;
  c6 = c5;
  std::cout << c5 << std::endl << std::endl;

  c6 = 0;
  add<Complex>(1, 2);

  return 0;
}

実行結果

30
constructor: (10, -10)@0x22ccb0
constructor: (-2, 0)@0x22cca0
add<>()
constructor: (8, -10)@0x22cc90
(8, -10)@0x22cc90

operator+()
constructor: (18, -20)@0x22cc80
(18, -20)@0x22cc80

operator+(), substitution
constructor: (16, -20)@0x22cc70
constructor: (0, 0)@0x22cc60
copy constructor: (16, -20)@0x22cc50
(16, -20)@0x22cc70

constructor: (0, 0)@0x22cc30
copy constructor: (0, 0)@0x22cc40
constructor: (2, 0)@0x22cc10
constructor: (1, 0)@0x22cc00
constructor: (3, 0)@0x22cc20

operator+を外で2項関数として定義する理由は、
第一引数に関して暗黙の型変換を有効にするため。
ようするに、
Complex(0,1)+1 も
1+Complex(0,1) も書けるようにするため。

上の方はどうでもよくて、面白いのは最後のところ。
引数のデフォルト値と演算子オーバーロードができると、こんなこともできるんですね...