Hallo Andreas,
hab in der i'X Deinen Beitrag gelesen und Rainer hatte darüber ja auch schon geschrieben. Zuerst der Code
struct QPoint
{
int
x{}, y{};
};
struct point_int
{
int
x{}, y{};
inline constexpr point_int() noexcept = default;
inline constexpr point_int(const point_int&) noexcept = default;
inline constexpr point_int(point_int&&) noexcept = default;
inline constexpr point_int(const int _x, const int _y) noexcept : x{_x}, y{_y} {}
inline constexpr point_int(const QPoint& q) noexcept : x{q.x}, y{q.y} {} // 1
inline constexpr auto operator<=>(const point_int&) const noexcept = default;
//inline constexpr bool operator==(const point_int&) const noexcept = default; // 2
//inline constexpr auto operator<=>(const QPoint& q) const noexcept { return (*this) <=> point_int{q.x, q.y}; } // 3
//inline constexpr bool operator==(const QPoint& q) const noexcept { return (*this) == point_int{q.x, q.y}; } // 4
template <typename Char, typename Traits = std::char_traits<Char>>
friend std::basic_ostream<Char, Traits>& operator<<(std::basic_ostream<Char, Traits>& stream, const point_int& arg) noexcept
{
stream << '(' << arg.x << ',' << arg.y << ')';
return stream;
}
};
template <typename Type>
struct point
{
using value_type = Type;
value_type
x{}, y{};
inline constexpr point() noexcept = default;
inline constexpr point(const point&) noexcept = default;
inline constexpr point(point&&) noexcept = default;
template <typename From>
inline constexpr point(const point<From>& arg) noexcept : x{value_type(arg.x)}, y{value_type(arg.y)} {}
template <typename X, typename Y>
inline constexpr point(const X& _x, const Y& _y) noexcept : x{value_type(_x)}, y{value_type(_y)} {}
inline constexpr auto operator<=>(const point&) const noexcept = default;
inline constexpr bool operator==(const point&) const noexcept = default;
/*
template <typename Other>
inline constexpr auto operator<=>(const point<Other>& arg) const noexcept
{
using compare_type = std::common_type_t<Type, Other>;
return point<compare_type>{*this} <=> point<compare_type>{arg};
}
template <typename Other>
inline constexpr bool operator==(const point<Other>& arg) const noexcept
{
using compare_type = std::common_type_t<Type, Other>;
return point<compare_type>{*this} == point<compare_type>{arg};
}
*/
template <typename Char, typename Traits = std::char_traits<Char>>
friend std::basic_ostream<Char, Traits>& operator<<(std::basic_ostream<Char, Traits>& stream, const point& arg) noexcept
{
stream << '(' << arg.x << ',' << arg.y << ')';
return stream;
}
};
int main(const int argc, const char** args)
{
std::cout << std::boolalpha;
if (argc == 3)
{
{
const int
a1=std::atoi(args[1]),
a2=std::atoi(args[2]);
const point_int
a{a1, a2},
b{a1, a2};
const QPoint
q{a1, a2};
std::cout << a << std::endl;
std::cout << b << std::endl;
std::cout<< (a==b) << std::endl;
std::cout<< (b==a) << std::endl;
std::cout<< (a==q) << std::endl;
std::cout<< (q==a) << std::endl;
}
std::cout << std::endl;
{
const double
a1=std::atof(args[1]),
a2=std::atof(args[2]);
const point<double>
a{a1, a2};
const point<int>
b{a1, a2};
std::cout << a << std::endl;
std::cout << b << std::endl;
std::cout<< (a==b) << std::endl; // 5
std::cout<< (b==a) << std::endl; // 6
}
}
return EXIT_SUCCESS;
}
jetzt mein Senf.
In dem einfachen Fall point_int, also die Typen von QPoint und point_int sind gleich, kann man sich die Ausformulierung der Operatoren == und <=> sparen - Dein Listing 8. Da gibt es 2 Möglichkeiten:
- Man implementiert einen Konvertierungs-ctor für QPoint und kann sich dann sogar den ==-Op sparen (2), da das hier hinreichend (primitiv) ist.
- Möglicherweise ist aber nicht gewünscht den cvt-ctor zu haben. Also (1) raus und (2+3+4) rein. (3+4) konvertieren manuell QPoint nach point_int und rufen die default-Ops auf. (2+4) müssen in diesem Fall aber vorhanden sein, sonst gibts 'n Compilerfehler.
Nun wird ja heutzutage kein vernünftiger Mensch mehr sowas mit festen Typen implementieren - also 'n Template. Doch dann gibst ne kleine Überaschung. Der Aufruf
./Test 1 1
liefert das richtige Ergebniss
(1,1)
(1,1)
true
true
true
true
(1,1)
(1,1)
true
true
jedoch
./Test 1 1000000000000000000
hingegen
(1,-1486618624)
(1,-1486618624)
true
true
true
true
(1,1e+18)
(1,-2147483648)
false
true
Was ist passiert? Klar, in (5+6) wird einmal point<int> nach point<double> konvertiert und dann verglichen und einmal anders herum: point<double> nach point<int> und dies verglichen. Durch den Überlauf von int sind das natürlich 2 verschiedene Werte. Indem man die beiden auskommentierten Ops aktiviert, kann man dieses ungewünschte Verhalten abstellen, da dann immer gleiche Typen verglichen werden - alles natürlich nur im Rahmen der Genauigkeit bei den Konvertierungen:
(1,-1486618624)
(1,-1486618624)
true
true
true
true
(1,1e+18)
(1,-2147483648)
false
false
cu
pre alpha
Das Posting wurde vom Benutzer editiert (26.02.2021 15:48).