From 6f69ab8e29523a50a744338712a873339be95a22 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Vladim=C3=ADr=20Vondru=C5=A1?= Date: Fri, 26 Jan 2024 11:24:03 +0100 Subject: [PATCH] Math: make const Vector operators constexpr. Need to make a constexpr style data for the UI library and it involves various multiplications and such, so took that as an opportunity to enable constexpr on all operators. No other functions such as max() so far, as I don't really need those yet. After a few abandoned iterations that involved adding constexpr overloads only to the Vector2, Vector3 and Vector4 subclasses I ended up with a rather minimal solution that makes the base Vector constexpr already, and just about 50 extra lines in total. In the original code from 2010, to avoid redundant code, the const operations were delegating to compound assignment operations, i.e. operator*() being implemented by making a copy of itself and then delegating to operator*=(). Thus, as far as a Debug build is concerned, one extra indirection for each. The new solution is *also* one indirection (which is needed in order to expand the variadic sequence) so it's not worse in Debug in any way, however it's one indirection less in the Vector2, Vector3 and Vector4 subclasses as there it delegates directly to the internal implementation instead of the base class operator. On GCC at least, there's no measurable impact on build times either -- the whole project builds in ~2:22 both before and after this change. The way the change is done also allows the new code to be compiled out if C++14 constexpr is enabled, where the functions would simply delegate to the compound assignments. I'm not planning to touch that any time soon either. --- src/Magnum/Math/Test/ColorTest.cpp | 13 + src/Magnum/Math/Test/Vector2Test.cpp | 8 + src/Magnum/Math/Test/Vector3Test.cpp | 8 + src/Magnum/Math/Test/Vector4Test.cpp | 8 + src/Magnum/Math/Test/VectorTest.cpp | 174 ++++++++++++ src/Magnum/Math/Vector.h | 384 +++++++++++++++------------ 6 files changed, 423 insertions(+), 172 deletions(-) diff --git a/src/Magnum/Math/Test/ColorTest.cpp b/src/Magnum/Math/Test/ColorTest.cpp index 5d00ace53..674f4f4ed 100644 --- a/src/Magnum/Math/Test/ColorTest.cpp +++ b/src/Magnum/Math/Test/ColorTest.cpp @@ -1088,6 +1088,19 @@ void ColorTest::multiplyDivideIntegral() { result, unless MAGNUM_VECTORn_OPERATOR_IMPLEMENTATION() is used */ CORRADE_COMPARE(-1.5f*vector3, multiplied3); CORRADE_COMPARE(-1.5f*vector4, multiplied4); + + constexpr Color3i cvector3{32, 10, -6}; + constexpr Color4i cvector4{32, 10, -6, 2}; + constexpr Color3i ca31 = cvector3*-1.5f; + constexpr Color4i ca41 = cvector4*-1.5f; + /* On MSVC 2015 this picks an int*Vector2i overload, leading to a wrong + result, unless MAGNUM_VECTORn_OPERATOR_IMPLEMENTATION() is used */ + constexpr Color3i ca32 = -1.5f*cvector3; + constexpr Color4i ca42 = -1.5f*cvector4; + CORRADE_COMPARE(ca31, multiplied3); + CORRADE_COMPARE(ca41, multiplied4); + CORRADE_COMPARE(ca32, multiplied3); + CORRADE_COMPARE(ca42, multiplied4); } void ColorTest::strictWeakOrdering() { diff --git a/src/Magnum/Math/Test/Vector2Test.cpp b/src/Magnum/Math/Test/Vector2Test.cpp index 74bf98bd9..248825293 100644 --- a/src/Magnum/Math/Test/Vector2Test.cpp +++ b/src/Magnum/Math/Test/Vector2Test.cpp @@ -261,6 +261,14 @@ void Vector2Test::multiplyDivideIntegral() { /* On MSVC 2015 this picks an int*Vector2i overload, leading to a wrong result, unless MAGNUM_VECTORn_OPERATOR_IMPLEMENTATION() is used */ CORRADE_COMPARE(-1.5f*vector, multiplied); + + constexpr Vector2i cvector{32, -6}; + constexpr Vector2i ca1 = cvector*-1.5f; + /* On MSVC 2015 this picks an int*Vector2i overload, leading to a wrong + result, unless MAGNUM_VECTORn_OPERATOR_IMPLEMENTATION() is used */ + constexpr Vector2i ca2 = -1.5f*cvector; + CORRADE_COMPARE(ca1, multiplied); + CORRADE_COMPARE(ca2, multiplied); } void Vector2Test::strictWeakOrdering() { diff --git a/src/Magnum/Math/Test/Vector3Test.cpp b/src/Magnum/Math/Test/Vector3Test.cpp index d125d686f..e55151181 100644 --- a/src/Magnum/Math/Test/Vector3Test.cpp +++ b/src/Magnum/Math/Test/Vector3Test.cpp @@ -285,6 +285,14 @@ void Vector3Test::multiplyDivideIntegral() { /* On MSVC 2015 this picks an int*Vector2i overload, leading to a wrong result, unless MAGNUM_VECTORn_OPERATOR_IMPLEMENTATION() is used */ CORRADE_COMPARE(-1.5f*vector, multiplied); + + constexpr Vector3i cvector{32, 10, -6}; + constexpr Vector3i ca1 = cvector*-1.5f; + /* On MSVC 2015 this picks an int*Vector2i overload, leading to a wrong + result, unless MAGNUM_VECTORn_OPERATOR_IMPLEMENTATION() is used */ + constexpr Vector3i ca2 = -1.5f*cvector; + CORRADE_COMPARE(ca1, multiplied); + CORRADE_COMPARE(ca2, multiplied); } void Vector3Test::strictWeakOrdering() { diff --git a/src/Magnum/Math/Test/Vector4Test.cpp b/src/Magnum/Math/Test/Vector4Test.cpp index 5b9e13478..d26546676 100644 --- a/src/Magnum/Math/Test/Vector4Test.cpp +++ b/src/Magnum/Math/Test/Vector4Test.cpp @@ -299,6 +299,14 @@ void Vector4Test::multiplyDivideIntegral() { /* On MSVC 2015 this picks an int*Vector2i overload, leading to a wrong result, unless MAGNUM_VECTORn_OPERATOR_IMPLEMENTATION() is used */ CORRADE_COMPARE(-1.5f*vector, multiplied); + + constexpr Vector4i cvector{32, 10, -6, 2}; + constexpr Vector4i ca1 = cvector*-1.5f; + /* On MSVC 2015 this picks an int*Vector2i overload, leading to a wrong + result, unless MAGNUM_VECTORn_OPERATOR_IMPLEMENTATION() is used */ + constexpr Vector4i ca2 = -1.5f*cvector; + CORRADE_COMPARE(ca1, multiplied); + CORRADE_COMPARE(ca2, multiplied); } void Vector4Test::planeEquationThreePoints() { diff --git a/src/Magnum/Math/Test/VectorTest.cpp b/src/Magnum/Math/Test/VectorTest.cpp index 1b8907911..23bc638c2 100644 --- a/src/Magnum/Math/Test/VectorTest.cpp +++ b/src/Magnum/Math/Test/VectorTest.cpp @@ -407,7 +407,9 @@ void VectorTest::promotedNegated() { constexpr Vector4 a{1.0f, -3.0f, 5.0f, -10.0f}; constexpr Vector4 promotedA = +a; + constexpr Vector4 negatedA = -a; CORRADE_COMPARE(promotedA, a); + CORRADE_COMPARE(negatedA, (Vector4{-1.0f, 3.0f, -5.0f, 10.0f})); } void VectorTest::addSubtract() { @@ -426,6 +428,15 @@ void VectorTest::addSubtract() { CORRADE_COMPARE(&(v -= b), &v); CORRADE_COMPARE(v, a); } + + constexpr Vector4 ca{1.0f, -3.0f, 5.0f, -10.0f}; + constexpr Vector4 cb{7.5f, 33.0f, -15.0f, 0.0f}; + constexpr Vector4 cd = ca + cb; + CORRADE_COMPARE(cd, c); + + constexpr Vector4 cc{8.5f, 30.0f, -10.0f, -10.0f}; + constexpr Vector4 ce = cc - cb; + CORRADE_COMPARE(ce, a); } void VectorTest::multiplyDivide() { @@ -447,10 +458,24 @@ void VectorTest::multiplyDivide() { CORRADE_COMPARE(v, vector); } + constexpr Vector4 cvector{1.0f, 2.0f, 3.0f, 4.0f}; + constexpr Vector4 ca1 = cvector*-1.5f; + constexpr Vector4 ca2 = -1.5f*cvector; + CORRADE_COMPARE(ca1, multiplied); + CORRADE_COMPARE(ca2, multiplied); + + constexpr Vector4 cmultiplied{-1.5f, -3.0f, -4.5f, -6.0f}; + constexpr Vector4 cb = cmultiplied/-1.5f; + CORRADE_COMPARE(cb, vector); + /* Divide a vector with a scalar and invert */ const Vector4 divisor{1.0f, 2.0f, -4.0f, 8.0f}; const Vector4 result{1.0f, 0.5f, -0.25f, 0.125f}; CORRADE_COMPARE(1.0f/divisor, result); + + constexpr Vector4 cdivisor(1.0f, 2.0f, -4.0f, 8.0f); + constexpr Vector4 ce = 1.0f/cdivisor; + CORRADE_COMPARE(ce, result); } void VectorTest::multiplyDivideIntegral() { @@ -472,6 +497,16 @@ void VectorTest::multiplyDivideIntegral() { CORRADE_COMPARE(v, vector); } + constexpr Vector4i cvector{32, 10, -6, 2}; + constexpr Vector4i ca1 = cvector*-1.5f; + constexpr Vector4i ca2 = -1.5f*cvector; + CORRADE_COMPARE(ca1, multiplied); + CORRADE_COMPARE(ca2, multiplied); + + constexpr Vector4i cmultiplied{-48, -15, 9, -3}; + constexpr Vector4i cb = cmultiplied/-1.5f; + CORRADE_COMPARE(cb, cvector); + /* Using integer vector as divisor is not supported */ } @@ -493,6 +528,14 @@ void VectorTest::multiplyDivideComponentWise() { CORRADE_COMPARE(&(v /= multiplier), &v); CORRADE_COMPARE(v, vec); } + + constexpr Vector4 cvec{1.0f, 2.0f, 3.0f, 4.0f}; + constexpr Vector4 cmultiplier{7.0f, -4.0f, -1.5f, 1.0f}; + constexpr Vector4 cmultiplied{7.0f, -8.0f, -4.5f, 4.0f}; + constexpr Vector4 ca = cvec*cmultiplier; + constexpr Vector4 cb = cmultiplied/cmultiplier; + CORRADE_COMPARE(ca, multiplied); + CORRADE_COMPARE(cb, vec); } void VectorTest::multiplyDivideComponentWiseIntegral() { @@ -515,6 +558,17 @@ void VectorTest::multiplyDivideComponentWiseIntegral() { CORRADE_COMPARE(v, vec); } + constexpr Vector4i cvec{7, 2, -16, -1}; + constexpr Vector4 cmultiplier{2.0f, -1.5f, 0.5f, 10.0f}; + constexpr Vector4i ca1 = cvec*cmultiplier; + constexpr Vector4i ca2 = cmultiplier*cvec; + CORRADE_COMPARE(ca1, multiplied); + CORRADE_COMPARE(ca2, multiplied); + + constexpr Vector4i cmultiplied{14, -3, -8, -10}; + constexpr Vector4i cb = cmultiplied/cmultiplier; + CORRADE_COMPARE(cb, cvec); + /* Using integer vector as divisor is not supported */ } @@ -535,6 +589,13 @@ void VectorTest::modulo() { CORRADE_COMPARE(&(v %= b), &v); CORRADE_COMPARE(v, (Vector4i{0, 3, 63, -2})); } + + constexpr Vector4i ca{4, 13, 255, -6}; + constexpr Vector4i cb{2, 5, 64, -4}; + constexpr Vector4i cc = ca % 2; + constexpr Vector4i cd = ca % cb; + CORRADE_COMPARE(cc, (Vector4i{0, 1, 1, 0})); + CORRADE_COMPARE(cd, (Vector4i{0, 3, 63, -2})); } void VectorTest::bitwise() { @@ -563,6 +624,17 @@ void VectorTest::bitwise() { CORRADE_COMPARE(v, (Vector4i{255, 165, -254, -44})); } + constexpr Vector4i ca{85, 240, -241, 33}; + constexpr Vector4i cb{170, 85, 13, -11}; + constexpr Vector4i cd = ~ca; + constexpr Vector4i ce = ca & cb; + constexpr Vector4i cf = ca | cb; + constexpr Vector4i cg = ca ^ cb; + CORRADE_COMPARE(cd, (Vector4i{-86, -241, 240, -34})); + CORRADE_COMPARE(ce, (Vector4i{0, 80, 13, 33})); + CORRADE_COMPARE(cf, (Vector4i{255, 245, -241, -11})); + CORRADE_COMPARE(cg, (Vector4i{255, 165, -254, -44})); + const Vector4i c{7, 32, 1, 15}; CORRADE_COMPARE(c << 2, (Vector4i{28, 128, 4, 60})); { @@ -577,6 +649,12 @@ void VectorTest::bitwise() { CORRADE_COMPARE(&(v >>= 2), &v); CORRADE_COMPARE(v, (Vector4i{1, 8, 0, 3})); } + + constexpr Vector4i cc{7, 32, 1, 15}; + constexpr Vector4i ch = cc << 2; + constexpr Vector4i ci = cc >> 2; + CORRADE_COMPARE(ch, (Vector4i{28, 128, 4, 60})); + CORRADE_COMPARE(ci, (Vector4i{1, 8, 0, 3})); } void VectorTest::dot() { @@ -855,6 +933,11 @@ void VectorTest::subclass() { } CORRADE_COMPARE(-Vec2(-2.0f, 5.0f), Vec2(2.0f, -5.0f)); + { + constexpr Vec2 ca{-2.0f, 5.0f}; + constexpr Vec2 cb = -ca; + CORRADE_COMPARE(cb, (Vec2{2.0f, -5.0f})); + } /* Addition / subtraction */ CORRADE_COMPARE(Vec2(-2.0f, 5.0f) + Vec2(1.0f, -3.0f), Vec2(-1.0f, 2.0f)); @@ -862,6 +945,11 @@ void VectorTest::subclass() { Vec2 a{-2.0f, 5.0f}; CORRADE_COMPARE(&(a += Vec2{1.0f, -3.0f}), &a); CORRADE_COMPARE(a, (Vec2{-1.0f, 2.0f})); + + constexpr Vec2 ca{-2.0f, 5.0f}; + constexpr Vec2 cb{1.0f, -3.0f}; + constexpr Vec2 cc = ca + cb; + CORRADE_COMPARE(cc, (Vec2{-1.0f, 2.0f})); } CORRADE_COMPARE(Vec2(-2.0f, 5.0f) - Vec2(1.0f, -3.0f), Vec2(-3.0f, 8.0f)); @@ -869,6 +957,11 @@ void VectorTest::subclass() { Vec2 a{-2.0f, 5.0f}; CORRADE_COMPARE(&(a -= Vec2{1.0f, -3.0f}), &a); CORRADE_COMPARE(a, (Vec2{-3.0f, 8.0f})); + + constexpr Vec2 ca{-2.0f, 5.0f}; + constexpr Vec2 cb{1.0f, -3.0f}; + constexpr Vec2 cc = ca - cb; + CORRADE_COMPARE(cc, (Vec2{-3.0f, 8.0f})); } /* Multiplication and division with a scalar */ @@ -878,6 +971,12 @@ void VectorTest::subclass() { Vec2 a{-2.0f, 5.0f}; CORRADE_COMPARE(&(a *= 2.0f), &a); CORRADE_COMPARE(a, (Vec2{-4.0f, 10.0f})); + + constexpr Vec2 ca{-2.0f, 5.0f}; + constexpr Vec2 cb1 = ca*2.0f; + constexpr Vec2 cb2 = 2.0f*ca; + CORRADE_COMPARE(cb1, (Vec2{-4.0f, 10.0f})); + CORRADE_COMPARE(cb2, (Vec2{-4.0f, 10.0f})); } CORRADE_COMPARE(Vec2(-2.0f, 5.0f)/0.5f, Vec2(-4.0f, 10.0f)); @@ -886,6 +985,12 @@ void VectorTest::subclass() { Vec2 a{-2.0f, 5.0f}; CORRADE_COMPARE(&(a /= 0.5f), &a); CORRADE_COMPARE(a, (Vec2{-4.0f, 10.0f})); + + constexpr Vec2 ca{-2.0f, 5.0f}; + constexpr Vec2 cb1 = ca/0.5f; + constexpr Vec2 cb2 = 2.0f/ca; + CORRADE_COMPARE(cb1, (Vec2{-4.0f, 10.0f})); + CORRADE_COMPARE(cb2, (Vec2{-1.0f, 0.4f})); } /* Multiplication/division with an integer scalar */ @@ -895,6 +1000,12 @@ void VectorTest::subclass() { Vec2i a{2, 4}; CORRADE_COMPARE(&(a *= 1.5f), &a); CORRADE_COMPARE(a, (Vec2i{3, 6})); + + constexpr Vec2i ca{2, 4}; + constexpr Vec2i cb1 = ca*1.5f; + constexpr Vec2i cb2 = 1.5f*ca; + CORRADE_COMPARE(cb1, (Vec2i{3, 6})); + CORRADE_COMPARE(cb2, (Vec2i{3, 6})); } { @@ -907,6 +1018,10 @@ void VectorTest::subclass() { Vec2i a{2, 4}; CORRADE_COMPARE(&(a /= (2.0f/3.0f)), &a); CORRADE_COMPARE(a, (Vec2i{3, 6})); + + constexpr Vec2i ca{2, 4}; + constexpr Vec2i cb = ca/(2.0f/3.0f); + CORRADE_COMPARE(cb, (Vec2i{3, 6})); } /* Multiplication and division with a vector */ @@ -915,6 +1030,11 @@ void VectorTest::subclass() { Vec2 a{-2.0f, 5.0f}; CORRADE_COMPARE(&(a *= Vec2{1.5f, -2.0f}), &a); CORRADE_COMPARE(a, (Vec2{-3.0f, -10.0f})); + + constexpr Vec2 ca{-2.0f, 5.0f}; + constexpr Vec2 cb{1.5f, -2.0f}; + constexpr Vec2 cc = ca*cb; + CORRADE_COMPARE(cc, (Vec2{-3.0f, -10.0f})); } CORRADE_COMPARE(Vec2(-2.0f, 5.0f)/Vec2(2.0f/3.0f, -0.5f), Vec2(-3.0f, -10.0f)); @@ -922,6 +1042,11 @@ void VectorTest::subclass() { Vec2 a{-2.0f, 5.0f}; CORRADE_COMPARE(&(a /= Vec2{2.0f/3.0f, -0.5f}), &a); CORRADE_COMPARE(a, (Vec2{-3.0f, -10.0f})); + + constexpr Vec2 ca{-2.0f, 5.0f}; + constexpr Vec2 cb{2.0f/3.0f, -0.5f}; + constexpr Vec2 cc = ca/cb; + CORRADE_COMPARE(cc, (Vec2{-3.0f, -10.0f})); } /* Multiplication/division with an integer vector */ @@ -931,6 +1056,13 @@ void VectorTest::subclass() { Vec2i a{2, 4}; CORRADE_COMPARE(&(a *= Vec2{-1.5f, 0.5f}), &a); CORRADE_COMPARE(a, (Vec2i{-3, 2})); + + constexpr Vec2i ca{2, 4}; + constexpr Vec2 cb{-1.5f, 0.5f}; + constexpr Vec2i cc1 = ca*cb; + constexpr Vec2i cc2 = cb*ca; + CORRADE_COMPARE(cc1, (Vec2i{-3, 2})); + CORRADE_COMPARE(cc2, (Vec2i{-3, 2})); } { @@ -943,6 +1075,11 @@ void VectorTest::subclass() { Vec2i a{2, 4}; CORRADE_COMPARE(&(a /= Vec2{-2.0f/3.0f, 2.0f}), &a); CORRADE_COMPARE(a, (Vec2i{-3, 2})); + + constexpr Vec2i ca{2, 4}; + constexpr Vec2 cb{-2.0f/3.0f, 2.0f}; + constexpr Vec2i cc = ca/cb; + CORRADE_COMPARE(cc, (Vec2i{-3, 2})); } /* Modulo operations */ @@ -951,6 +1088,10 @@ void VectorTest::subclass() { Vec2i a{4, 13}; CORRADE_COMPARE(&(a %= 2), &a); CORRADE_COMPARE(a, (Vec2i{0, 1})); + + constexpr Vec2i ca{4, 13}; + constexpr Vec2i cb = ca % 2; + CORRADE_COMPARE(cb, (Vec2i{0, 1})); } CORRADE_COMPARE(Vec2i(4, 13) % Vec2i(2, 5), Vec2i(0, 3)); @@ -958,10 +1099,20 @@ void VectorTest::subclass() { Vec2i a{4, 13}; CORRADE_COMPARE(&(a %= Vec2i{2, 5}), &a); CORRADE_COMPARE(a, (Vec2i{0, 3})); + + constexpr Vec2i ca{4, 13}; + constexpr Vec2i cb{2, 5}; + constexpr Vec2i cc = ca % cb; + CORRADE_COMPARE(cc, (Vec2i{0, 3})); } /* Unary bitwise operations */ CORRADE_COMPARE(~Vec2i(85, 240), Vec2i(-86, -241)); + { + constexpr Vec2i ca{85, 240}; + constexpr Vec2i cb = ~ca; + CORRADE_COMPARE(cb, (Vec2i{-86, -241})); + } /* Bitwise AND, OR and XOR */ CORRADE_COMPARE(Vec2i(85, 240) & Vec2i(170, 85), Vec2i(0, 80)); @@ -969,6 +1120,11 @@ void VectorTest::subclass() { Vec2i a{85, 240}; CORRADE_COMPARE(&(a &= Vec2i{170, 85}), &a); CORRADE_COMPARE(a, (Vec2i{0, 80})); + + constexpr Vec2i ca{85, 240}; + constexpr Vec2i cb{170, 85}; + constexpr Vec2i cc = ca & cb; + CORRADE_COMPARE(cc, (Vec2i{0, 80})); } CORRADE_COMPARE(Vec2i(85, 240) | Vec2i(170, 85), Vec2i(255, 245)); @@ -976,6 +1132,11 @@ void VectorTest::subclass() { Vec2i a{85, 240}; CORRADE_COMPARE(&(a |= Vec2i{170, 85}), &a); CORRADE_COMPARE(a, (Vec2i{255, 245})); + + constexpr Vec2i ca{85, 240}; + constexpr Vec2i cb{170, 85}; + constexpr Vec2i cc = ca | cb; + CORRADE_COMPARE(cc, (Vec2i{255, 245})); } CORRADE_COMPARE(Vec2i(85, 240) ^ Vec2i(170, 85), Vec2i(255, 165)); @@ -983,6 +1144,11 @@ void VectorTest::subclass() { Vec2i a{85, 240}; CORRADE_COMPARE(&(a ^= Vec2i{170, 85}), &a); CORRADE_COMPARE(a, (Vec2i{255, 165})); + + constexpr Vec2i ca{85, 240}; + constexpr Vec2i cb{170, 85}; + constexpr Vec2i cc = ca ^ cb; + CORRADE_COMPARE(cc, (Vec2i{255, 165})); } /* Bit shift */ @@ -991,6 +1157,10 @@ void VectorTest::subclass() { Vec2i a{7, 32}; CORRADE_COMPARE(&(a <<= 2), &a); CORRADE_COMPARE(a, (Vec2i{28, 128})); + + constexpr Vec2i ca{7, 32}; + constexpr Vec2i cb = ca << 2; + CORRADE_COMPARE(cb, (Vec2i{28, 128})); } CORRADE_COMPARE(Vec2i(7, 32) >> 2, Vec2i(1, 8)); @@ -998,6 +1168,10 @@ void VectorTest::subclass() { Vec2i a{7, 32}; CORRADE_COMPARE(&(a >>= 2), &a); CORRADE_COMPARE(a, (Vec2i{1, 8})); + + constexpr Vec2i ca{7, 32}; + constexpr Vec2i cb = ca >> 2; + CORRADE_COMPARE(cb, (Vec2i{1, 8})); } /* Functions */ diff --git a/src/Magnum/Math/Vector.h b/src/Magnum/Math/Vector.h index 7feee1aef..8e00bd8cc 100644 --- a/src/Magnum/Math/Vector.h +++ b/src/Magnum/Math/Vector.h @@ -356,11 +356,13 @@ template class Vector { * @see @ref flipped(), @ref Vector2::perpendicular() */ #ifdef DOXYGEN_GENERATING_OUTPUT - Vector + constexpr Vector #else - template typename std::enable_if::value, Vector>::type + template constexpr typename std::enable_if::value, Vector>::type #endif - operator-() const; + operator-() const { + return negateInternal(typename Containers::Implementation::GenerateSequence::Type{}); + } /** * @brief Add and assign a vector @@ -381,9 +383,8 @@ template class Vector { * * @see @ref operator+=(), @ref sum() */ - Vector operator+(const Vector& other) const { - /* MSVC 2015 and 2017 treat the copy as a const if {} is used */ - return Vector(*this) += other; + constexpr Vector operator+(const Vector& other) const { + return addInternal(other, typename Containers::Implementation::GenerateSequence::Type{}); } /** @@ -405,9 +406,8 @@ template class Vector { * * @see @ref operator-=() */ - Vector operator-(const Vector& other) const { - /* MSVC 2015 and 2017 treat the copy as a const if {} is used */ - return Vector(*this) -= other; + constexpr Vector operator-(const Vector& other) const { + return subtractInternal(other, typename Containers::Implementation::GenerateSequence::Type{}); } /** @@ -433,9 +433,8 @@ template class Vector { * @ref operator*=(T), @ref operator*(T, const Vector&), * @ref operator*(FloatingPoint) const */ - Vector operator*(T scalar) const { - /* MSVC 2015 and 2017 treat the copy as a const if {} is used */ - return Vector(*this) *= scalar; + constexpr Vector operator*(T scalar) const { + return multiplyInternal(scalar, typename Containers::Implementation::GenerateSequence::Type{}); } /** @@ -443,7 +442,7 @@ template class Vector { * * Same as @ref operator*(T) const. */ - friend Vector operator*( + constexpr friend Vector operator*( #ifdef DOXYGEN_GENERATING_OUTPUT T #else @@ -479,13 +478,12 @@ template class Vector { * is done in floating-point. */ #ifdef DOXYGEN_GENERATING_OUTPUT - template Vector + template constexpr Vector #else - template typename std::enable_if::value && std::is_floating_point::value, Vector>::type + template constexpr typename std::enable_if::value && std::is_floating_point::value, Vector>::type #endif operator*(FloatingPoint scalar) const { - /* MSVC 2015 and 2017 treat the copy as a const if {} is used */ - return Vector(*this) *= scalar; + return multiplyIntegerInternal(scalar, typename Containers::Implementation::GenerateSequence::Type{}); } /** @@ -498,9 +496,9 @@ template class Vector { which is (and the two don't conflict, apparently, so both are present) */ #ifdef DOXYGEN_GENERATING_OUTPUT - template friend Vector + template friend constexpr Vector #else - template friend typename std::enable_if::value && std::is_floating_point::value, Vector>::type + template friend constexpr typename std::enable_if::value && std::is_floating_point::value, Vector>::type #endif operator*(FloatingPoint scalar, const Vector& vector) { return vector*scalar; @@ -529,9 +527,8 @@ template class Vector { * @ref operator/=(T), @ref operator/(T, const Vector&), * @ref operator/(FloatingPoint) const */ - Vector operator/(T scalar) const { - /* MSVC 2015 and 2017 treat the copy as a const if {} is used */ - return Vector(*this) /= scalar; + constexpr Vector operator/(T scalar) const { + return divideInternal(scalar, typename Containers::Implementation::GenerateSequence::Type{}); } /** @@ -542,7 +539,7 @@ template class Vector { * @f] * @see @ref operator/(T) const */ - friend Vector operator/( + friend constexpr Vector operator/( #ifdef DOXYGEN_GENERATING_OUTPUT T #else @@ -550,12 +547,7 @@ template class Vector { #endif scalar, const Vector& vector) { - Vector out; - - for(std::size_t i = 0; i != size; ++i) - out._data[i] = scalar/vector._data[i]; - - return out; + return divideInternal(scalar, vector, typename Containers::Implementation::GenerateSequence::Type{}); } /** @@ -583,13 +575,12 @@ template class Vector { * in floating-point. */ #ifdef DOXYGEN_GENERATING_OUTPUT - template Vector + template Vector constexpr #else - template typename std::enable_if::value && std::is_floating_point::value, Vector>::type + template constexpr typename std::enable_if::value && std::is_floating_point::value, Vector>::type #endif operator/(FloatingPoint scalar) const { - /* MSVC 2015 and 2017 treat the copy as a const if {} is used */ - return Vector(*this) /= scalar; + return divideIntegerInternal(scalar, typename Containers::Implementation::GenerateSequence::Type{}); } /** @@ -615,9 +606,8 @@ template class Vector { * @ref operator*(const Vector&) const, * @ref product() */ - Vector operator*(const Vector& other) const { - /* MSVC 2015 and 2017 treat the copy as a const if {} is used */ - return Vector(*this) *= other; + constexpr Vector operator*(const Vector& other) const { + return multiplyInternal(other, typename Containers::Implementation::GenerateSequence::Type{}); } /** @@ -651,9 +641,8 @@ template class Vector { #ifndef DOXYGEN_GENERATING_OUTPUT , class Integral = T, typename std::enable_if::value && std::is_floating_point::value>::type* = nullptr #endif - > Vector operator*(const Vector& other) const { - /* MSVC 2015 and 2017 treat the copy as a const if {} is used */ - return Vector(*this) *= other; + > constexpr Vector operator*(const Vector& other) const { + return multiplyIntegerInternal(other, typename Containers::Implementation::GenerateSequence::Type{}); } /** @@ -670,7 +659,7 @@ template class Vector { #ifndef DOXYGEN_GENERATING_OUTPUT , class FloatingPoint = T, typename std::enable_if::value && std::is_floating_point::value>::type* = nullptr #endif - > Vector operator*(const Vector& other) const { + > constexpr Vector operator*(const Vector& other) const { return other**this; } @@ -696,9 +685,8 @@ template class Vector { * @see @ref operator/(T) const, @ref operator/=(const Vector&), * @ref operator/(const Vector&) const */ - Vector operator/(const Vector& other) const { - /* MSVC 2015 and 2017 treat the copy as a const if {} is used */ - return Vector(*this) /= other; + constexpr Vector operator/(const Vector& other) const { + return divideInternal(other, typename Containers::Implementation::GenerateSequence::Type{}); } /** @@ -729,13 +717,12 @@ template class Vector { * floating-point type to have a floating-point result. */ #ifdef DOXYGEN_GENERATING_OUTPUT - template Vector + template constexpr Vector #else - template typename std::enable_if::value && std::is_floating_point::value, Vector>::type + template constexpr typename std::enable_if::value && std::is_floating_point::value, Vector>::type #endif operator/(const Vector& other) const { - /* MSVC 2015 and 2017 treat the copy as a const if {} is used */ - return Vector(*this) /= other; + return divideIntegerInternal(other, typename Containers::Implementation::GenerateSequence::Type{}); } /** @@ -761,13 +748,12 @@ template class Vector { * Enabled only for integral types. */ #ifdef DOXYGEN_GENERATING_OUTPUT - Vector + constexpr Vector #else - template typename std::enable_if::value, Vector>::type + template constexpr typename std::enable_if::value, Vector>::type #endif operator%(T scalar) const { - /* MSVC 2015 and 2017 treat the copy as a const if {} is used */ - return Vector(*this) %= scalar; + return moduloInternal(scalar, typename Containers::Implementation::GenerateSequence::Type{}); } /** @@ -793,13 +779,12 @@ template class Vector { * Enabled only for integral types. */ #ifdef DOXYGEN_GENERATING_OUTPUT - Vector + constexpr Vector #else - template typename std::enable_if::value, Vector>::type + template constexpr typename std::enable_if::value, Vector>::type #endif operator%(const Vector& other) const { - /* MSVC 2015 and 2017 treat the copy as a const if {} is used */ - return Vector(*this) %= other; + return moduloInternal(other, typename Containers::Implementation::GenerateSequence::Type{}); } /** @@ -808,17 +793,12 @@ template class Vector { * Enabled only for integral types. */ #ifdef DOXYGEN_GENERATING_OUTPUT - Vector + constexpr Vector #else - template typename std::enable_if::value, Vector>::type + template constexpr typename std::enable_if::value, Vector>::type #endif operator~() const { - Vector out; - - for(std::size_t i = 0; i != size; ++i) - out._data[i] = ~_data[i]; - - return out; + return invertInternal(typename Containers::Implementation::GenerateSequence::Type{}); } /** @@ -844,13 +824,12 @@ template class Vector { * Enabled only for integral types. */ #ifdef DOXYGEN_GENERATING_OUTPUT - Vector + constexpr Vector #else - template typename std::enable_if::value, Vector>::type + template constexpr typename std::enable_if::value, Vector>::type #endif operator&(const Vector& other) const { - /* MSVC 2015 and 2017 treat the copy as a const if {} is used */ - return Vector(*this) &= other; + return andInternal(other, typename Containers::Implementation::GenerateSequence::Type{}); } /** @@ -876,13 +855,12 @@ template class Vector { * Enabled only for integral types. */ #ifdef DOXYGEN_GENERATING_OUTPUT - Vector + constexpr Vector #else - template typename std::enable_if::value, Vector>::type + template constexpr typename std::enable_if::value, Vector>::type #endif operator|(const Vector& other) const { - /* MSVC 2015 and 2017 treat the copy as a const if {} is used */ - return Vector(*this) |= other; + return orInternal(other, typename Containers::Implementation::GenerateSequence::Type{}); } /** @@ -908,13 +886,12 @@ template class Vector { * Enabled only for integral types. */ #ifdef DOXYGEN_GENERATING_OUTPUT - Vector + constexpr Vector #else - template typename std::enable_if::value, Vector>::type + template constexpr typename std::enable_if::value, Vector>::type #endif operator^(const Vector& other) const { - /* MSVC 2015 and 2017 treat the copy as a const if {} is used */ - return Vector(*this) ^= other; + return xorInternal(other, typename Containers::Implementation::GenerateSequence::Type{}); } /** @@ -940,14 +917,13 @@ template class Vector { * Enabled only for integral types. */ #ifdef DOXYGEN_GENERATING_OUTPUT - Vector operator<<(T shift) const + constexpr Vector operator<<(T shift) const #else - template typename std::enable_if::value, Vector>::type + template constexpr typename std::enable_if::value, Vector>::type operator<<(typename std::common_type::type shift) const #endif { - /* MSVC 2015 and 2017 treat the copy as a const if {} is used */ - return Vector(*this) <<= shift; + return shiftLeftInternal(shift, typename Containers::Implementation::GenerateSequence::Type{}); } /** @@ -973,14 +949,13 @@ template class Vector { * Enabled only for integral types. */ #ifdef DOXYGEN_GENERATING_OUTPUT - Vector operator>>(T shift) const + constexpr Vector operator>>(T shift) const #else template - typename std::enable_if::value, Vector>::type operator>>(typename std::common_type::type shift) const + constexpr typename std::enable_if::value, Vector>::type operator>>(typename std::common_type::type shift) const #endif { - /* MSVC 2015 and 2017 treat the copy as a const if {} is used */ - return Vector(*this) >>= shift; + return shiftRightInternal(shift, typename Containers::Implementation::GenerateSequence::Type{}); } /** @@ -1168,6 +1143,86 @@ template class Vector { builds */ T _data[size]; + /* Implementation for constexpr operators. Not SFINAE-restricted for + integers or integers + floats, not marked as inline friends, no + std::common_type workarounds for scalars as the callers do all + that already. Protected as they're used directly by subclasses + through the MAGNUM_VECTOR_SUBCLASS_IMPLEMENTATION() macro to speed + up debug builds. */ + template constexpr Vector negateInternal(Containers::Implementation::Sequence) const { + /* All these explicitly cast to T because with e.g. Vector2s it + would otherwise cause narrowing warnings because stupid C + promotion rules make e.g. `short + short` result in an int */ + return {T(-_data[sequence])...}; + } + template constexpr Vector addInternal(const Vector& other, Containers::Implementation::Sequence) const { + return {T(_data[sequence] + other._data[sequence])...}; + } + template constexpr Vector subtractInternal(const Vector& other, Containers::Implementation::Sequence) const { + return {T(_data[sequence] - other._data[sequence])...}; + } + template constexpr Vector multiplyInternal(T scalar, Containers::Implementation::Sequence) const { + return {T(_data[sequence]*scalar)...}; + } + template constexpr Vector multiplyIntegerInternal(FloatingPoint scalar, Containers::Implementation::Sequence) const { + /* This has to cast even without C promotion rules in effect, to + convert a floating-point result back to an integer */ + return {T(_data[sequence]*scalar)...}; + } + template constexpr Vector divideInternal(T scalar, Containers::Implementation::Sequence) const { + return {T(_data[sequence]/scalar)...}; + } + template constexpr Vector divideIntegerInternal(FloatingPoint scalar, Containers::Implementation::Sequence) const { + /* This has to cast even without C promotion rules in effect, to + convert a floating-point result back to an integer */ + return {T(_data[sequence]/scalar)...}; + } + template constexpr static Vector divideInternal(T scalar, const Vector& vector, Containers::Implementation::Sequence) { + return {T(scalar/vector._data[sequence])...}; + } + template constexpr Vector multiplyInternal(const Vector& other, Containers::Implementation::Sequence) const { + return {T(_data[sequence]*other._data[sequence])...}; + } + template constexpr Vector multiplyIntegerInternal(const Vector& other, Containers::Implementation::Sequence) const { + /* This has to cast even without C promotion rules in effect, to + convert a floating-point result back to an integer */ + return {T(_data[sequence]*other._data[sequence])...}; + } + template constexpr Vector divideInternal(const Vector& other, Containers::Implementation::Sequence) const { + return {T(_data[sequence]/other._data[sequence])...}; + } + template constexpr Vector divideIntegerInternal(const Vector& other, Containers::Implementation::Sequence) const { + /* This has to cast even without C promotion rules in effect, to + convert a floating-point result back to an integer */ + return {T(_data[sequence]/other._data[sequence])...}; + } + + template constexpr Vector moduloInternal(T scalar, Containers::Implementation::Sequence) const { + return {T(_data[sequence] % scalar)...}; + } + template constexpr Vector moduloInternal(const Math::Vector& other, Containers::Implementation::Sequence) const { + return {T(_data[sequence] % other._data[sequence])...}; + } + + template constexpr Vector invertInternal(Containers::Implementation::Sequence) const { + return {T(~_data[sequence])...}; + } + template constexpr Vector andInternal(const Math::Vector& other, Containers::Implementation::Sequence) const { + return {T(_data[sequence] & other._data[sequence])...}; + } + template constexpr Vector orInternal(const Math::Vector& other, Containers::Implementation::Sequence) const { + return {T(_data[sequence] | other._data[sequence])...}; + } + template constexpr Vector xorInternal(const Math::Vector& other, Containers::Implementation::Sequence) const { + return {T(_data[sequence] ^ other._data[sequence])...}; + } + template constexpr Vector shiftLeftInternal(typename std::common_type::type shift, Containers::Implementation::Sequence) const { + return {T(_data[sequence] << shift)...}; + } + template constexpr Vector shiftRightInternal(typename std::common_type::type shift, Containers::Implementation::Sequence) const { + return {T(_data[sequence] >> shift)...}; + } + private: #ifndef DOXYGEN_GENERATING_OUTPUT /* Since I added deprecated aliases to Shaders::VectorGL, this FUCKING @@ -1270,178 +1325,178 @@ extern template MAGNUM_EXPORT Debug& operator<<(Debug&, const Vector<4, Double>& #endif #ifndef DOXYGEN_GENERATING_OUTPUT -#define MAGNUM_VECTOR_SUBCLASS_IMPLEMENTATION(size, Type) \ - static Type& from(T* data) { \ - return *reinterpret_cast*>(data); \ +#define MAGNUM_VECTOR_SUBCLASS_IMPLEMENTATION(size, Type_) \ + static Type_& from(T* data) { \ + return *reinterpret_cast*>(data); \ } \ - static const Type& from(const T* data) { \ - return *reinterpret_cast*>(data); \ + static const Type_& from(const T* data) { \ + return *reinterpret_cast*>(data); \ } \ - template constexpr static Type pad(const Math::Vector& a, T value = T()) { \ + template constexpr static Type_ pad(const Math::Vector& a, T value = T()) { \ return Math::Vector::pad(a, value); \ } \ \ - constexpr Type operator+() const { \ + constexpr Type_ operator+() const { \ return Math::Vector::operator+(); \ } \ - template typename std::enable_if::value, Type>::type \ + template constexpr typename std::enable_if::value, Type_>::type \ operator-() const { \ - return Math::Vector::operator-(); \ + return Math::Vector::negateInternal(typename Containers::Implementation::GenerateSequence::Type{}); \ } \ - Type& operator+=(const Math::Vector& other) { \ + Type_& operator+=(const Math::Vector& other) { \ Math::Vector::operator+=(other); \ return *this; \ } \ - Type operator+(const Math::Vector& other) const { \ - return Math::Vector::operator+(other); \ + constexpr Type_ operator+(const Math::Vector& other) const { \ + return Math::Vector::addInternal(other, typename Containers::Implementation::GenerateSequence::Type{}); \ } \ - Type& operator-=(const Math::Vector& other) { \ + Type_& operator-=(const Math::Vector& other) { \ Math::Vector::operator-=(other); \ return *this; \ } \ - Type operator-(const Math::Vector& other) const { \ - return Math::Vector::operator-(other); \ + constexpr Type_ operator-(const Math::Vector& other) const { \ + return Math::Vector::subtractInternal(other, typename Containers::Implementation::GenerateSequence::Type{}); \ } \ \ - Type& operator*=(T scalar) { \ + Type_& operator*=(T scalar) { \ Math::Vector::operator*=(scalar); \ return *this; \ } \ - Type operator*(T scalar) const { \ - return Math::Vector::operator*(scalar); \ + constexpr Type_ operator*(T scalar) const { \ + return Math::Vector::multiplyInternal(scalar, typename Containers::Implementation::GenerateSequence::Type{}); \ } \ - friend Type operator*(typename std::common_type::type scalar, const Type& vector) { \ + friend constexpr Type_ operator*(typename std::common_type::type scalar, const Type_& vector) { \ return scalar*static_cast&>(vector); \ } \ - template typename std::enable_if::value && std::is_floating_point::value, Type&>::type operator*=(FloatingPoint scalar) { \ + template typename std::enable_if::value && std::is_floating_point::value, Type_&>::type operator*=(FloatingPoint scalar) { \ Math::Vector::operator*=(scalar); \ return *this; \ } \ - template typename std::enable_if::value && std::is_floating_point::value, Type>::type operator*(FloatingPoint scalar) const { \ - return Math::Vector::operator*(scalar); \ + template constexpr typename std::enable_if::value && std::is_floating_point::value, Type_>::type operator*(FloatingPoint scalar) const { \ + return Math::Vector::multiplyIntegerInternal(scalar, typename Containers::Implementation::GenerateSequence::Type{}); \ } \ - template friend typename std::enable_if::value && std::is_floating_point::value, Type>::type operator*(FloatingPoint scalar, const Type& vector) { \ + template friend constexpr typename std::enable_if::value && std::is_floating_point::value, Type_>::type operator*(FloatingPoint scalar, const Type_& vector) { \ return scalar*static_cast&>(vector); \ } \ \ - Type& operator/=(T scalar) { \ + Type_& operator/=(T scalar) { \ Math::Vector::operator/=(scalar); \ return *this; \ } \ - Type operator/(T scalar) const { \ - return Math::Vector::operator/(scalar); \ + constexpr Type_ operator/(T scalar) const { \ + return Math::Vector::divideInternal(scalar, typename Containers::Implementation::GenerateSequence::Type{}); \ } \ - friend Type operator/(typename std::common_type::type scalar, const Type& vector) { \ + friend constexpr Type_ operator/(typename std::common_type::type scalar, const Type_& vector) { \ return scalar/static_cast&>(vector); \ } \ - template typename std::enable_if::value && std::is_floating_point::value, Type&>::type operator/=(FloatingPoint scalar) { \ + template typename std::enable_if::value && std::is_floating_point::value, Type_&>::type operator/=(FloatingPoint scalar) { \ Math::Vector::operator/=(scalar); \ return *this; \ } \ - template typename std::enable_if::value && std::is_floating_point::value, Type>::type operator/(FloatingPoint scalar) const { \ - return Math::Vector::operator/(scalar); \ + template constexpr typename std::enable_if::value && std::is_floating_point::value, Type_>::type operator/(FloatingPoint scalar) const { \ + return Math::Vector::divideIntegerInternal(scalar, typename Containers::Implementation::GenerateSequence::Type{}); \ } \ \ - Type& operator*=(const Math::Vector& other) { \ + Type_& operator*=(const Math::Vector& other) { \ Math::Vector::operator*=(other); \ return *this; \ } \ - Type operator*(const Math::Vector& other) const { \ - return Math::Vector::operator*(other); \ + constexpr Type_ operator*(const Math::Vector& other) const { \ + return Math::Vector::multiplyInternal(other, typename Containers::Implementation::GenerateSequence::Type{}); \ } \ - template typename std::enable_if::value && std::is_floating_point::value, Type&>::type operator*=(const Math::Vector& other) { \ + template typename std::enable_if::value && std::is_floating_point::value, Type_&>::type operator*=(const Math::Vector& other) { \ Math::Vector::operator*=(other); \ return *this; \ } \ - template::value && std::is_floating_point::value>::type* = nullptr> Type operator*(const Math::Vector& other) const { \ - return Math::Vector::operator*(other); \ + template::value && std::is_floating_point::value>::type* = nullptr> constexpr Type_ operator*(const Math::Vector& other) const { \ + return Math::Vector::multiplyIntegerInternal(other, typename Containers::Implementation::GenerateSequence::Type{}); \ } \ - template::value && std::is_floating_point::value>::type* = nullptr> Type operator*(const Math::Vector& other) const { \ - return Math::Vector::operator*(other); \ + template::value && std::is_floating_point::value>::type* = nullptr> constexpr Type_ operator*(const Math::Vector& other) const { \ + return other**this; \ } \ \ - Type& operator/=(const Math::Vector& other) { \ + Type_& operator/=(const Math::Vector& other) { \ Math::Vector::operator/=(other); \ return *this; \ } \ - Type operator/(const Math::Vector& other) const { \ - return Math::Vector::operator/(other); \ + constexpr Type_ operator/(const Math::Vector& other) const { \ + return Math::Vector::divideInternal(other, typename Containers::Implementation::GenerateSequence::Type{}); \ } \ - template typename std::enable_if::value && std::is_floating_point::value, Type&>::type operator/=(const Math::Vector& other) { \ + template typename std::enable_if::value && std::is_floating_point::value, Type_&>::type operator/=(const Math::Vector& other) { \ Math::Vector::operator/=(other); \ return *this; \ } \ - template typename std::enable_if::value && std::is_floating_point::value, Type>::type operator/(const Math::Vector& other) const { \ - return Math::Vector::operator/(other); \ + template constexpr typename std::enable_if::value && std::is_floating_point::value, Type_>::type operator/(const Math::Vector& other) const { \ + return Math::Vector::divideIntegerInternal(other, typename Containers::Implementation::GenerateSequence::Type{}); \ } \ \ - template typename std::enable_if::value, Type&>::type operator%=(T scalar) { \ + template typename std::enable_if::value, Type_&>::type operator%=(T scalar) { \ Math::Vector::operator%=(scalar); \ return *this; \ } \ - template typename std::enable_if::value, Type>::type operator%(T scalar) const { \ - return Math::Vector::operator%(scalar); \ + template constexpr typename std::enable_if::value, Type_>::type operator%(T scalar) const { \ + return Math::Vector::moduloInternal(scalar, typename Containers::Implementation::GenerateSequence::Type{}); \ } \ - template typename std::enable_if::value, Type&>::type operator%=(const Math::Vector& other) { \ + template typename std::enable_if::value, Type_&>::type operator%=(const Math::Vector& other) { \ Math::Vector::operator%=(other); \ return *this; \ } \ - template typename std::enable_if::value, Type>::type operator%(const Math::Vector& other) const { \ - return Math::Vector::operator%(other); \ + template constexpr typename std::enable_if::value, Type_>::type operator%(const Math::Vector& other) const { \ + return Math::Vector::moduloInternal(other, typename Containers::Implementation::GenerateSequence::Type{}); \ } \ \ - template typename std::enable_if::value, Type>::type operator~() const { \ - return Math::Vector::operator~(); \ + template constexpr typename std::enable_if::value, Type_>::type operator~() const { \ + return Math::Vector::invertInternal(typename Containers::Implementation::GenerateSequence::Type{}); \ } \ - template typename std::enable_if::value, Type&>::type operator&=(const Math::Vector& other) { \ + template typename std::enable_if::value, Type_&>::type operator&=(const Math::Vector& other) { \ Math::Vector::operator&=(other); \ return *this; \ } \ - template typename std::enable_if::value, Type>::type operator&(const Math::Vector& other) const { \ - return Math::Vector::operator&(other); \ + template constexpr typename std::enable_if::value, Type_>::type operator&(const Math::Vector& other) const { \ + return Math::Vector::andInternal(other, typename Containers::Implementation::GenerateSequence::Type{}); \ } \ - template typename std::enable_if::value, Type&>::type operator|=(const Math::Vector& other) { \ + template typename std::enable_if::value, Type_&>::type operator|=(const Math::Vector& other) { \ Math::Vector::operator|=(other); \ return *this; \ } \ - template typename std::enable_if::value, Type>::type operator|(const Math::Vector& other) const { \ - return Math::Vector::operator|(other); \ + template constexpr typename std::enable_if::value, Type_>::type operator|(const Math::Vector& other) const { \ + return Math::Vector::orInternal(other, typename Containers::Implementation::GenerateSequence::Type{}); \ } \ - template typename std::enable_if::value, Type&>::type operator^=(const Math::Vector& other) { \ + template typename std::enable_if::value, Type_&>::type operator^=(const Math::Vector& other) { \ Math::Vector::operator^=(other); \ return *this; \ } \ - template typename std::enable_if::value, Type>::type operator^(const Math::Vector& other) const { \ - return Math::Vector::operator^(other); \ + template constexpr typename std::enable_if::value, Type_>::type operator^(const Math::Vector& other) const { \ + return Math::Vector::xorInternal(other, typename Containers::Implementation::GenerateSequence::Type{}); \ } \ - template typename std::enable_if::value, Type&>::type operator<<=(typename std::common_type::type shift) { \ + template typename std::enable_if::value, Type_&>::type operator<<=(typename std::common_type::type shift) { \ Math::Vector::operator<<=(shift); \ return *this; \ } \ - template typename std::enable_if::value, Type>::type operator<<(typename std::common_type::type shift) const { \ - return Math::Vector::operator<<(shift); \ + template constexpr typename std::enable_if::value, Type_>::type operator<<(typename std::common_type::type shift) const { \ + return Math::Vector::shiftLeftInternal(shift, typename Containers::Implementation::GenerateSequence::Type{}); \ } \ - template typename std::enable_if::value, Type&>::type operator>>=(typename std::common_type::type shift) { \ + template typename std::enable_if::value, Type_&>::type operator>>=(typename std::common_type::type shift) { \ Math::Vector::operator>>=(shift); \ return *this; \ } \ - template typename std::enable_if::value, Type>::type operator>>(typename std::common_type::type shift) const { \ - return Math::Vector::operator>>(shift); \ + template constexpr typename std::enable_if::value, Type_>::type operator>>(typename std::common_type::type shift) const { \ + return Math::Vector::shiftRightInternal(shift, typename Containers::Implementation::GenerateSequence::Type{}); \ } \ \ - template typename std::enable_if::value, Type>::type normalized() const { \ + template typename std::enable_if::value, Type_>::type normalized() const { \ return Math::Vector::normalized(); \ } \ - template typename std::enable_if::value, Type>::type resized(T length) const { \ + template typename std::enable_if::value, Type_>::type resized(T length) const { \ return Math::Vector::resized(length); \ } \ - template typename std::enable_if::value, Type>::type projected(const Math::Vector& other) const { \ + template typename std::enable_if::value, Type_>::type projected(const Math::Vector& other) const { \ return Math::Vector::projected(other); \ } \ - template typename std::enable_if::value, Type>::type projectedOntoNormalized(const Math::Vector& other) const { \ + template typename std::enable_if::value, Type_>::type projectedOntoNormalized(const Math::Vector& other) const { \ return Math::Vector::projectedOntoNormalized(other); \ } \ - constexpr Type flipped() const { \ + constexpr Type_ flipped() const { \ return Math::Vector::flipped(); \ } #endif @@ -1455,12 +1510,12 @@ extern template MAGNUM_EXPORT Debug& operator<<(Debug&, const Vector<4, Double>& to return a correct subtype. See VectorTest::multiplyDivideIntegral(), VectorTest::subclass() and corresponding cases in Vector2Test, Vector3Test, Vector4Test and ColorTest for regression tests. */ -template inline typename std::enable_if::value && std::is_floating_point::value, Vector>::type operator*(FloatingPoint scalar, const Vector& vector) { +template constexpr typename std::enable_if::value && std::is_floating_point::value, Vector>::type operator*(FloatingPoint scalar, const Vector& vector) { return vector*scalar; } #define MAGNUM_VECTORn_OPERATOR_IMPLEMENTATION(size, Type) \ - template inline typename std::enable_if::value && std::is_floating_point::value, Type>::type operator*(FloatingPoint scalar, const Type& vector) { \ + template constexpr typename std::enable_if::value && std::is_floating_point::value, Type>::type operator*(FloatingPoint scalar, const Type& vector) { \ return vector*scalar; \ } #endif @@ -1501,21 +1556,6 @@ template inline BitVector Vector::oper return out; } -template -#ifdef DOXYGEN_GENERATING_OUTPUT -inline Vector -#else -template inline typename std::enable_if::value, Vector>::type -#endif -Vector::operator-() const { - Vector out; - - for(std::size_t i = 0; i != size; ++i) - out._data[i] = -_data[i]; - - return out; -} - template #ifdef DOXYGEN_GENERATING_OUTPUT inline Vector