diff --git a/3-Operators/1-Intro.ipynb b/3-Operators/1-Intro.ipynb index 559f5e16fdd36c0cf63bca997c5284c7802fc755..6fe74ecbec667ef4a4a55733e85b95deaae8ca97 100644 --- a/3-Operators/1-Intro.ipynb +++ b/3-Operators/1-Intro.ipynb @@ -144,7 +144,9 @@ "\n", "## Overloading an operator\n", "\n", - "To overload an operator, the syntax is just the keyword **operator** followed by the operator to overload." + "To overload an operator, the syntax is just the keyword **operator** followed by the operator to overload. In the following we will just replace the `Add` method by `operator+`.\n", + "\n", + "The following code illustrate how to do so... but unfortunately doesn't run with Xeus CLing (you may play with it [@Coliru](https://coliru.stacked-crooked.com/a/765b9dc1e2b73c71)).\n" ] }, { @@ -153,33 +155,75 @@ "metadata": {}, "outputs": [], "source": [ - "class Vector2\n", + "// DOESN'T RUN WITH XEUS-CLING\n", + "\n", + "#include <iostream>\n", + "\n", + "class VectorPlus\n", "{\n", " public :\n", "\n", - " Vector2(double x, double y, double z);\n", + " VectorPlus(double x, double y, double z);\n", " \n", - " Vector2() = default;\n", + " VectorPlus() = default;\n", " \n", " void Print() const;\n", " \n", - " // I would rather put the definition outside but Xeus-cling doesn't seem to accept this.\n", - " Vector2 operator+(const Vector2& v) const\n", - " {\n", - " Vector2 ret;\n", - " ret.x_ = x_ + v.x_;\n", - " ret.y_ = y_ + v.y_;\n", - " ret.z_ = z_ + v.z_;\n", - " \n", - " return ret;\n", - " }\n", + " // Friendship as free function operator+ wouldn't otherwise be allowed to access data attributes.\n", + " friend VectorPlus operator+(const VectorPlus& v1, const VectorPlus& v2);\n", "\n", " private :\n", " \n", " double x_ = 0.;\n", " double y_ = 0.;\n", " double z_ = 0.;\n", - "}; " + "}; \n", + "\n", + "VectorPlus::VectorPlus(double x, double y, double z)\n", + ": x_(x),\n", + "y_(y),\n", + "z_(z)\n", + "{ }\n", + "\n", + "\n", + "void VectorPlus::Print() const\n", + "{\n", + " std::cout << \"(\" << x_ << \", \" << y_ << \", \" << z_ << \")\" << std::endl;\n", + "}\n", + "\n", + "\n", + "VectorPlus operator+(const VectorPlus& v1, const VectorPlus& v2) \n", + "{ \n", + " // Provides a symmetric implementation of operator +: both vectors are at the same level! \n", + " VectorPlus ret;\n", + " ret.x_ = v1.x_ + v2.x_;\n", + " ret.y_ = v1.y_ + v2.y_;\n", + " ret.z_ = v1.z_ + v2.z_;\n", + " \n", + " return ret;\n", + "}\n", + "\n", + "\n", + "int main(int argc, char** argv)\n", + "{\n", + " VectorPlus v1(3., 5., 7.);\n", + " VectorPlus v2(7., 5., 3.);\n", + " \n", + " VectorPlus v3 = v1 + v2; // Nicer syntax!\n", + " v3.Print();\n", + " \n", + " VectorPlus v4 = operator+(v1, v2); // but \"usual\" method syntax is possible as well\n", + " v4.Print();\n", + " \n", + " return EXIT_SUCCESS;\n", + "}" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "It should be noted that for most operators it is also possible to define them as a class method instead:" ] }, { @@ -188,18 +232,33 @@ "metadata": {}, "outputs": [], "source": [ - "// How the definition outside the class would look like: nothing new or surprising here...\n", - "// Not accepted by Xeus-cling ().\n", - "\n", - "Vector2 Vector2::operator+(const Vector2& v) const\n", + "class VectorPlusAsMethod\n", "{\n", - " Vector2 ret;\n", - " ret.x_ = x_ + v.x_;\n", - " ret.y_ = y_ + v.y_;\n", - " ret.z_ = z_ + v.z_;\n", + " public :\n", "\n", - " return ret;\n", - "}\n" + " VectorPlusAsMethod(double x, double y, double z);\n", + " \n", + " VectorPlusAsMethod() = default;\n", + " \n", + " void Print() const;\n", + " \n", + " // I would rather put the definition outside but Xeus-cling doesn't seem to accept this.\n", + " VectorPlusAsMethod operator+(const VectorPlusAsMethod& v) const\n", + " {\n", + " VectorPlusAsMethod ret;\n", + " ret.x_ = x_ + v.x_;\n", + " ret.y_ = y_ + v.y_;\n", + " ret.z_ = z_ + v.z_;\n", + " \n", + " return ret;\n", + " }\n", + "\n", + " private :\n", + " \n", + " double x_ = 0.;\n", + " double y_ = 0.;\n", + " double z_ = 0.;\n", + "}; " ] }, { @@ -208,7 +267,7 @@ "metadata": {}, "outputs": [], "source": [ - "Vector2::Vector2(double x, double y, double z)\n", + "VectorPlusAsMethod::VectorPlusAsMethod(double x, double y, double z)\n", ": x_(x),\n", "y_(y),\n", "z_(z)\n", @@ -223,7 +282,7 @@ "source": [ "#include <iostream>\n", "\n", - "void Vector2::Print() const\n", + "void VectorPlusAsMethod::Print() const\n", "{\n", " std::cout << \"(\" << x_ << \", \" << y_ << \", \" << z_ << \")\" << std::endl;\n", "}" @@ -236,11 +295,14 @@ "outputs": [], "source": [ "{\n", - " Vector2 v1(3., 5., 7.);\n", - " Vector2 v2(7., 5., 3.);\n", + " VectorPlusAsMethod v1(3., 5., 7.);\n", + " VectorPlusAsMethod v2(7., 5., 3.);\n", " \n", - " Vector2 v3 = v1 + v2;\n", + " VectorPlusAsMethod v3 = v1 + v2;\n", " v3.Print();\n", + " \n", + " VectorPlusAsMethod v4 = v1.operator+(v2); // but \"usual\" method syntax is possible as well\n", + " v4.Print();\n", "}" ] }, @@ -248,80 +310,9 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "We see in the definition of the `operator+` that both `Vector2` added aren't symmetric: one is the data attribute while the other is the data attribute of an object given as an argument.\n", - "\n", - "As a side note, please remark the `operator+` implementation is able to reach the private data attributes of the argument `v`; this means the private status is set **at class level** and not at object level.\n", - "\n", - "\n", - "It is actually possible to define the operator as a free function, thus providing a more symmetric implementation:\n", - "\n", - "**Xeus-cling issue**: cling doesn't accept operator definition outside of class; please use [@Coliru](https://coliru.stacked-crooked.com/a/626efa4fb6a02915):" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "// Xeus-cling issue: doesn't compile!\n", - "\n", - "#include <iostream>\n", - "\n", - "class Vector3\n", - "{\n", - " public :\n", - "\n", - " Vector3(double x, double y, double z);\n", - " \n", - " Vector3() = default;\n", - " \n", - " void Print() const;\n", - " \n", - " friend Vector3 operator+(const Vector3& v1, const Vector3& v2);\n", - "\n", - " private :\n", - " \n", - " double x_ = 0.;\n", - " double y_ = 0.;\n", - " double z_ = 0.;\n", - "}; \n", - "\n", - "Vector3::Vector3(double x, double y, double z)\n", - ": x_(x),\n", - "y_(y),\n", - "z_(z)\n", - "{ }\n", + "We see here in the definition of the `operator+` that both `VectorPlusAsMethod` objects added aren't symmetric: one is the data attribute while the other is the data attribute of an object given as an argument. If is often advised to rather use the free function version to avoid this asymmetry, but it is mostly a matter of taste as both are working.\n", "\n", - "\n", - "void Vector3::Print() const\n", - "{\n", - " std::cout << \"(\" << x_ << \", \" << y_ << \", \" << z_ << \")\" << std::endl;\n", - "}\n", - "\n", - "\n", - "Vector3 operator+(const Vector3& v1, const Vector3& v2) \n", - "{ \n", - " // Provides a symmetric implementation of operator +: both vectors are at the same level! \n", - " Vector3 ret;\n", - " ret.x_ = v1.x_ + v2.x_;\n", - " ret.y_ = v1.y_ + v2.y_;\n", - " ret.z_ = v1.z_ + v2.z_;\n", - " \n", - " return ret;\n", - "}\n", - "\n", - "\n", - "int main(int argc, char** argv)\n", - "{\n", - " Vector3 v1(3., 5., 7.);\n", - " Vector3 v2(7., 5., 3.);\n", - " \n", - " Vector3 v3 = v1 + v2;\n", - " v3.Print();\n", - " \n", - " return EXIT_SUCCESS;\n", - "}" + "As a side note, please remark the `VectorPlusAsMethod::operator+` implementation is able to reach the private data attributes of the argument `v`; this means the private status is set **at class level** and not at object level." ] }, { @@ -340,20 +331,20 @@ "metadata": {}, "outputs": [], "source": [ - "class Vector4\n", + "class VectorPlusDouble\n", "{\n", " public :\n", "\n", - " Vector4(double x, double y, double z);\n", + " VectorPlusDouble(double x, double y, double z);\n", " \n", - " Vector4() = default;\n", + " VectorPlusDouble() = default;\n", " \n", " void Print() const;\n", " \n", " // Defined in the class declaration due to Xeus-cling limitation.\n", - " Vector4 operator+(double value) const\n", + " VectorPlusDouble operator+(double value) const\n", " {\n", - " Vector4 ret;\n", + " VectorPlusDouble ret;\n", " ret.x_ = x_ + value;\n", " ret.y_ = y_ + value;\n", " ret.z_ = z_ + value;\n", @@ -377,7 +368,7 @@ "source": [ "#include <iostream>\n", "\n", - "void Vector4::Print() const\n", + "void VectorPlusDouble::Print() const\n", "{\n", " std::cout << \"(\" << x_ << \", \" << y_ << \", \" << z_ << \")\" << std::endl;\n", "}" @@ -389,7 +380,7 @@ "metadata": {}, "outputs": [], "source": [ - "Vector4::Vector4(double x, double y, double z)\n", + "VectorPlusDouble::VectorPlusDouble(double x, double y, double z)\n", ": x_(x),\n", "y_(y),\n", "z_(z)\n", @@ -403,8 +394,8 @@ "outputs": [], "source": [ "{\n", - " Vector4 vector(5., 3.2, -1.);\n", - " Vector4 vector_plus_5 = vector + 5.;\n", + " VectorPlusDouble vector(5., 3.2, -1.);\n", + " VectorPlusDouble vector_plus_5 = vector + 5.;\n", " vector_plus_5.Print();\n", "}" ] @@ -423,8 +414,8 @@ "outputs": [], "source": [ "{\n", - " Vector4 vector(5., 3.2, -1.);\n", - " Vector4 vector_plus_5 = vector.operator+(5.);\n", + " VectorPlusDouble vector(5., 3.2, -1.);\n", + " VectorPlusDouble vector_plus_5 = vector.operator+(5.);\n", "}" ] }, @@ -442,8 +433,8 @@ "outputs": [], "source": [ "{\n", - " Vector4 vector(5., 3.2, -1.);\n", - " Vector4 vector_plus_5 = 5. + vector; // COMPILATION ERROR!\n", + " VectorPlusDouble vector(5., 3.2, -1.);\n", + " VectorPlusDouble vector_plus_5 = 5. + vector; // COMPILATION ERROR!\n", "}" ] }, @@ -451,7 +442,7 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "If you want it to be possible, you have to define the operator with arguments in both orders; you therefore need to use out-of-class prototype of the function (can't show it currently due to Xeus-cling limitation, available [@Coliru](https://coliru.stacked-crooked.com/a/bb8130aec13bdf26)).\n", + "If you want it to be possible, you have to define the operator with arguments in both orders; you therefore need to use out-of-class prototype of the function (can't show it currently due to Xeus-cling limitation, available [@Coliru](https://coliru.stacked-crooked.com/a/c03fc40e0f0a8ea0)).\n", "\n", "Of course, it is a **good practice** to define one in way of the other:" ] @@ -467,19 +458,19 @@ "#include <cstdlib>\n", "#include <iostream>\n", "\n", - "class Vector5\n", + "class VectorPlusDoubleCommutative\n", "{\n", " public :\n", "\n", - " Vector5(double x, double y, double z);\n", + " VectorPlusDoubleCommutative(double x, double y, double z);\n", " \n", - " Vector5() = default;\n", + " VectorPlusDoubleCommutative() = default;\n", " \n", " void Print() const;\n", " \n", - " friend Vector5 operator+(const Vector5& v, double value);\n", + " friend VectorPlusDoubleCommutative operator+(const VectorPlusDoubleCommutative& v, double value);\n", " \n", - " friend Vector5 operator+(double value, const Vector5& v);\n", + " friend VectorPlusDoubleCommutative operator+(double value, const VectorPlusDoubleCommutative& v);\n", "\n", " private :\n", " \n", @@ -490,13 +481,13 @@ "\n", "\n", "\n", - "void Vector5::Print() const\n", + "void VectorPlusDoubleCommutative::Print() const\n", "{\n", " std::cout << \"(\" << x_ << \", \" << y_ << \", \" << z_ << \")\" << std::endl;\n", "}\n", "\n", "\n", - "Vector5::Vector5(double x, double y, double z)\n", + "VectorPlusDoubleCommutative::VectorPlusDoubleCommutative(double x, double y, double z)\n", ": x_(x),\n", "y_(y),\n", "z_(z)\n", @@ -504,9 +495,9 @@ "\n", "\n", "\n", - "Vector5 operator+(const Vector5& v, double value)\n", + "VectorPlusDoubleCommutative operator+(const VectorPlusDoubleCommutative& v, double value)\n", "{\n", - " Vector5 ret;\n", + " VectorPlusDoubleCommutative ret;\n", " \n", " ret.x_ = v.x_ + value;\n", " ret.y_ = v.y_ + value;\n", @@ -516,17 +507,17 @@ "}\n", "\n", "\n", - "Vector5 operator+(double value, const Vector5& v)\n", + "VectorPlusDoubleCommutative operator+(double value, const VectorPlusDoubleCommutative& v)\n", "{\n", - " return v + value;\n", + " return v + value; // good practice: make it rely upon the other `operator+` defined!\n", "}\n", "\n", "\n", "int main(int argc, char** argv)\n", "{\n", - " Vector5 vector(5., 3.2, -1.);\n", - " Vector5 vector_plus_5 = vector + 5.;\n", - " Vector5 vector_plus_5_commutated = 5. + vector;\n", + " VectorPlusDoubleCommutative vector(5., 3.2, -1.);\n", + " VectorPlusDoubleCommutative vector_plus_5 = vector + 5.;\n", + " VectorPlusDoubleCommutative vector_plus_5_commutated = 5. + vector;\n", " \n", " vector_plus_5.Print();\n", " vector_plus_5_commutated.Print();\n", @@ -673,6 +664,8 @@ "metadata": {}, "outputs": [], "source": [ + "// Won't run in Xeus-cling, as it's not within the class declaration\n", + "\n", "explicit operator int() const;\n", "explicit operator double() const;" ]