|
// MSVC: /std:c++20 /Zc:__cplusplus /Zc:preprocessor (without the /Zc the language version reporting + macro work will fail!) |
|
// clang: -std=c++20 |
|
// gcc: -std=c++20 |
|
// |
|
// The code below uses compile-time feature detect preprocessor code to assist: |
|
// the code SHOULD compile with older C++ language version settings; this assertion has only been tested intermittantly |
|
// with GCC/clang/MSVC at godbolt, so YMMV for your C++17 and older. |
|
// |
|
// https://godbolt.org/z/67Wehevah |
|
// |
|
|
|
#include <initializer_list> |
|
#include <string> |
|
#include <iostream> |
|
#include <cstdarg> |
|
#include <cassert> |
|
|
|
|
|
// features: |
|
#define EXTRA_OVERLOADS 1 // see further below for comment block which mentions this one. That's a hint. ;-) |
|
|
|
|
|
#if defined(_MSC_VER) && !defined(__has_include) |
|
#define __has_include(arg) 0 |
|
#endif |
|
|
|
#if __has_include(<format>) |
|
# include <format> |
|
// clang still b0rks with: error: no member named 'format' in namespace 'std' -- when the CLI options don't include std c++20 at least! |
|
// The include file check above is not sufficient as clang will always have libstdc++'s format header file available... |
|
# if __cplusplus >= 202002L |
|
# define STD_FORMAT_IS_SUPPORTED 1 |
|
# endif |
|
#endif |
|
|
|
// https://stackoverflow.com/questions/65472035/checking-for-three-way-comparison-operator-support-at-compile-time |
|
#if __has_include(<compare>) |
|
# include <compare> |
|
# if defined(__cpp_lib_three_way_comparison) && __cpp_lib_three_way_comparison >= 201907 |
|
# define SPACESHIP_OPERATOR_IS_SUPPORTED 1 |
|
# endif |
|
#endif |
|
|
|
|
|
static int object_numero_ctr = 1; |
|
|
|
// instances of struct T have ids that increment per decade, i.e. 10,20,30,... |
|
// while the unit digit identifies the actual constructor used to instantiate that particular object instance, ergo, f.e. 13, 21, 31, ... |
|
static int mk_id(int preproc__counter__) { |
|
return (object_numero_ctr++) * 10 + preproc__counter__ + 1; // __COUNTER__ starts at 0 and we wish to start at 1 per object: unit digit identifies the actual constructor used: see below in the `struct T` definition. |
|
} |
|
|
|
|
|
static const char *currently_tested_expression = nullptr; |
|
|
|
|
|
#define TRACE_ARGS_1(_1) /**/ |
|
#define TRACE_ARGS_2(_1, _2) TRACE_ARGS_1(_1) << " / " << _2 |
|
#define TRACE_ARGS_3(_1, _2, _3) TRACE_ARGS_1(_1) << " / " << _2 << " / " << _3 |
|
#define TRACE_ARGS_4(_1, _2, _3, _4) TRACE_ARGS_1(_1) << " / " << _2 << " / " << _3 << " / " << _4 |
|
|
|
#define TRACE_ARGS_OVERRIDE(_1, _2, _3, _4, NAME, ...) NAME |
|
#define TRACE_ARG(...) TRACE_ARGS_OVERRIDE(__VA_ARGS__, TRACE_ARGS_4, TRACE_ARGS_3, TRACE_ARGS_2, TRACE_ARGS_1)(__VA_ARGS__) |
|
|
|
#define TRACE_THIS_ID() this->id |
|
|
|
#ifdef STD_FORMAT_IS_SUPPORTED |
|
#define TRACE(...) std::cout << std::format(" --> {}({})", __func__, TRACE_THIS_ID()) TRACE_ARG("" __VA_OPT__(,) __VA_ARGS__) << "\n" |
|
#else |
|
#define TRACE(...) std::cout << " --> " << __func__ << "(" << TRACE_THIS_ID() << ")" << /* TRACE_ARG("" __VA_OPT__(,) __VA_ARGS__) << */ "\n" |
|
#endif |
|
|
|
|
|
static void report_cpp_language_version(void) { |
|
std::cout << "C++ v" << __cplusplus << "\nC++ language version: "; |
|
|
|
if (__cplusplus == 202302L) std::cout << "C++23"; |
|
else if (__cplusplus == 202002L) std::cout << "C++20"; |
|
else if (__cplusplus == 201703L) std::cout << "C++17"; |
|
else if (__cplusplus == 201402L) std::cout << "C++14"; |
|
else if (__cplusplus == 201103L) std::cout << "C++11"; |
|
else if (__cplusplus == 199711L) std::cout << "C++98"; |
|
else std::cout << "pre-standard C++." << __cplusplus; |
|
std::cout << "\n"; |
|
} |
|
|
|
|
|
static void report_compiler_version(void) { |
|
#if defined(_MSC_VER) |
|
std::cout << "Microsoft CL v" << _MSC_VER << " (full: v" << _MSC_FULL_VER << ")"; |
|
#elif defined(__clang__) |
|
std::cout << "clang v" << __clang_version__; |
|
#elif defined(__GNUC__) |
|
std::cout << "gcc v" << __GNUC__ << "." << __GNUC_MINOR__ << " (v" << __VERSION__ << ")"; |
|
#elif defined(__VERSION__) |
|
std::cout << "??? v" << __VERSION__; |
|
#else |
|
std::cout << "???"; |
|
#endif |
|
std::cout << "\n"; |
|
} |
|
|
|
|
|
template <class M> |
|
struct T { |
|
int id; |
|
M a, b; |
|
|
|
// constructors & destructor |
|
T() |
|
: a{}, b{}, id(mk_id(__COUNTER__)) { |
|
TRACE("(constructor)"); |
|
} |
|
|
|
~T() { |
|
TRACE(); |
|
} |
|
|
|
T(M v1, M v2) |
|
: a{v1}, b{v2}, id(mk_id(__COUNTER__)) { |
|
TRACE("(constructor)"); |
|
} |
|
|
|
// copy constructor |
|
T(const T &v) |
|
: a{v.a}, b{v.b}, id(mk_id(__COUNTER__)) { |
|
TRACE("(copy constructor)"); |
|
} |
|
|
|
// move constructor |
|
T(T &&v) noexcept |
|
: a{std::move(v.a)}, b{std::move(v.b)}, id(mk_id(__COUNTER__)) { |
|
TRACE("(move constructor)", std::format("v:({})", v.id) ); |
|
} |
|
|
|
// --------------------------------------------------------- |
|
// a list of C++ operators that can be overloaded |
|
// |
|
// any of the following operators: + - * / % ^ & | ~ ! = < > += -= *= /= %= ^= &= |= << >> >>= <<= == != <= >= <=>(since C++20) && || ++ -- , ->* -> () [] |
|
|
|
// assignment operator |
|
T operator =(const T &rhs) { |
|
TRACE(std::format("rhs:({})", rhs.id)); |
|
a = rhs.a; |
|
b = rhs.b; |
|
return *this; |
|
} |
|
T operator =(T &&rhs) { |
|
TRACE(std::format("rhs:({})", rhs.id)); |
|
a = std::move(rhs.a); |
|
b = std::move(rhs.b); |
|
return *this; |
|
} |
|
|
|
// Arithmetic: |
|
// operator+ addition |
|
T operator+(T const & rhs) const { |
|
TRACE(std::format("rhs:({})", rhs.id)); |
|
return {a, b}; |
|
} |
|
// free function -> T operator+(T const & lhs, T const & rhs) |
|
|
|
// operator+ unary plus |
|
T operator+() const { |
|
TRACE("(unary +)"); |
|
auto v = a + 1; |
|
return v; |
|
} |
|
// free function -> T & operator+(T const & value) |
|
|
|
// operator+= addition assignment |
|
T & operator+=(T const & rhs) { |
|
TRACE(std::format("rhs:({})", rhs.id)); |
|
return *this; |
|
} |
|
// free function -> T & operator+=(T & lhs, T const & rhs) |
|
|
|
// operator++ increment |
|
// prefix ++T |
|
T & operator++() { |
|
TRACE("(prefix ++)"); |
|
return *this; |
|
} |
|
// postfix T++ |
|
T operator++(int) { |
|
TRACE("(postfix ++)"); |
|
return *this; |
|
} |
|
// free function -> T & operator++(T & value) -> prefix ++T |
|
// free function -> T operator++(T & value, int) -> postfix T++ |
|
|
|
// operator- subtraction |
|
T operator-(T const & rhs) const { |
|
TRACE(std::format("rhs:({})", rhs.id)); |
|
return *this; |
|
} |
|
// free function -> T operator-(T const & lhs, T const & rhs) |
|
|
|
// opeator- unary negation |
|
T operator-() const { |
|
TRACE("(unary minus)"); |
|
return *this; |
|
} |
|
// free function -> T operator-(T const & value) |
|
|
|
// operator-= subtraction assignment |
|
T & operator-=(T const & rhs) { |
|
return *this; |
|
} |
|
// free function -> T & operator-=(T & lhs, T const & rhs) |
|
|
|
// operator-- decrement |
|
// prefix --T |
|
T & operator--() { |
|
TRACE("(prefix --)"); |
|
return *this; |
|
} |
|
// postfix T-- |
|
T operator--(int) { |
|
TRACE("(postfix --)"); |
|
return *this; |
|
} |
|
// free function -> T & operator--(T & value) -> prefix --T |
|
// free function -> T operator--(T & value, int) -> postfix T-- |
|
|
|
// operator* multiplication |
|
T operator*(T const & rhs) const { |
|
TRACE(std::format("rhs:({})", rhs.id)); |
|
return {a, b}; |
|
} |
|
// free function -> T operator*(T const & lhs, T const & rhs) |
|
|
|
// operator*= multiplication assignment |
|
T & operator*=(T const & rhs) { |
|
TRACE(std::format("rhs:({})", rhs.id)); |
|
return *this; |
|
} |
|
// free function -> T & operator*=(T & lhs, T const & rhs) |
|
|
|
// operator/ division |
|
T operator/(T const & rhs) const { |
|
TRACE(std::format("rhs:({})", rhs.id)); |
|
return {a, b}; |
|
} |
|
// free function -> T operator/(T const & lhs, T const & rhs) |
|
|
|
// operator/= division assignment |
|
T & operator/=(T const & rhs) { |
|
TRACE(std::format("rhs:({})", rhs.id)); |
|
return *this; |
|
} |
|
// free function -> T & operator/=(T & lhs, T const & rhs) |
|
|
|
// operator% modulus/remainder |
|
T operator%(T const & rhs) const { |
|
TRACE(std::format("rhs:({})", rhs.id)); |
|
return {a, b}; |
|
} |
|
// free function -> T operator%(T const & lhs, T const & rhs) |
|
|
|
// operator%= modulus/remainder assignment |
|
T & operator%=(T const & rhs) { |
|
TRACE(std::format("rhs:({})", rhs.id)); |
|
return *this; |
|
} |
|
// free function -> T & operator%=(T & lhs, T const & rhs) |
|
|
|
// Bitwise |
|
// operator<< left shift |
|
T operator<<(size_t pos) const { |
|
TRACE(std::format("offset:{}", pos)); |
|
return {a, b}; |
|
} |
|
// free function -> T operator<<(T const & lhs, size_t pos) |
|
|
|
// operator<<= left shift assignment |
|
T & operator<<=(size_t pos) { |
|
TRACE(std::format("offset:{}", pos)); |
|
return *this; |
|
} |
|
// free function -> T & operator<<=(T & lhs, size_t pos) |
|
|
|
// operator>> right shift |
|
T operator>>(size_t pos) const { |
|
TRACE(std::format("offset:{}", pos)); |
|
return {a, b}; |
|
} |
|
// free function -> T operator>>(T const & lhs, size_t pos) |
|
|
|
// operator>>= right shift assignment |
|
T & operator>>=(size_t pos) { |
|
TRACE(std::format("offset:{}", pos)); |
|
return *this; |
|
} |
|
// free function -> T & operator>>=(T & lhs, size_t pos) |
|
|
|
// operator| or operator bitor bitwise or |
|
T operator|(T const & rhs) const { |
|
TRACE(std::format("rhs:({})", rhs.id)); |
|
return {a, b}; |
|
} |
|
// free function -> T operator|(T const & lhs, T const & rhs) |
|
|
|
// operator|= or operator or_eq bitwise or assignment |
|
T & operator|=(T const & rhs) { |
|
TRACE(std::format("rhs:({})", rhs.id)); |
|
return *this; |
|
} |
|
// free function -> T & operator|=(T & lhs, T const & rhs) |
|
|
|
// operator& or operator bitand bitwise and |
|
T operator&(T const & rhs) const { |
|
TRACE(std::format("rhs:({})", rhs.id)); |
|
return {a, b}; |
|
} |
|
// free function -> T operator&(T const & lhs, T const & rhs) |
|
|
|
// operator&= or operator and_eq bitwise and assignment |
|
T & operator&=(T const & rhs) { |
|
TRACE(std::format("rhs:({})", rhs.id)); |
|
return *this; |
|
} |
|
// free function -> T & operator&=(T & lhs, T const & rhs) |
|
|
|
// operator^ or operator xor bitwise xor |
|
T operator^(T const & rhs) const { |
|
TRACE(std::format("rhs:({})", rhs.id)); |
|
return {a, b}; |
|
} |
|
// free function -> T operator^(T const & lhs, T const & rhs) |
|
|
|
// operator^= or operator xor_eq bitwise xor assignment |
|
T & operator^=(T const & rhs) { |
|
TRACE(std::format("rhs:({})", rhs.id)); |
|
return *this; |
|
} |
|
// free function -> T & operator^=(T & lhs, T const & rhs) |
|
|
|
// operator~ one's compliment or bitwise not |
|
T operator~() const { |
|
TRACE(); |
|
return {a, b}; |
|
} |
|
// free function -> T operator~(T const & value) |
|
|
|
// Logical |
|
// operator== equality |
|
bool operator==(T const & rhs) const { |
|
TRACE(std::format("rhs:({})", rhs.id)); |
|
return a == rhs.a; |
|
} |
|
// free function -> bool operator==(T const & lhs, T const & rhs) |
|
|
|
// operator!= or operator not_eq inequality |
|
bool operator!=(T const & rhs) const { |
|
TRACE(std::format("rhs:({})", rhs.id)); |
|
return a != rhs.a; |
|
} |
|
// free function -> bool operator!=(T const & lhs, T const & rhs) |
|
|
|
// operator< less than |
|
bool operator<(T const & rhs) const { |
|
TRACE(std::format("rhs:({})", rhs.id)); |
|
return a < rhs.a; |
|
} |
|
// free function -> bool operator<(T const & lhs, T const & rhs) |
|
|
|
// operator<= less than or equal |
|
bool operator<=(T const & rhs) const { |
|
TRACE(std::format("rhs:({})", rhs.id)); |
|
return a <= rhs.a; |
|
} |
|
// free function -> bool operator<=(T const & lhs, T const & rhs) |
|
|
|
// operator> greater than |
|
bool operator>(T const & rhs) { |
|
TRACE(std::format("rhs:({})", rhs.id)); |
|
return a > rhs.a; |
|
} |
|
// free function -> bool operator>(T const & lhs, T const & rhs) |
|
|
|
// operator>= greater than or equal |
|
bool operator>=(T const & rhs) const { |
|
TRACE(std::format("rhs:({})", rhs.id)); |
|
return a >= rhs.a; |
|
} |
|
// free function -> bool operator>=(T const & lhs, T const & rhs) |
|
|
|
// operator&& or operator and logical and |
|
bool operator&&(T const & rhs) const { |
|
TRACE(std::format("rhs:({})", rhs.id)); |
|
return false; // despite this result, there won't be any 'shortcircuit evaluation'! |
|
} |
|
// free function -> bool operator&&(T const & lhs, T const & rhs) |
|
|
|
// operator|| or operator or logical or |
|
bool operator||(T const & rhs) const { |
|
TRACE(std::format("rhs:({})", rhs.id)); |
|
return true; // despite this result, there won't be any 'shortcircuit evaluation'! |
|
} |
|
// free function -> bool operator||(T const & lhs, T const & rhs) |
|
|
|
// operator! or operator not logical not |
|
bool operator!() const { |
|
TRACE(); |
|
return false; |
|
} |
|
// free function -> bool operator!(T const & value) const |
|
|
|
#ifdef SPACESHIP_OPERATOR_IS_SUPPORTED |
|
// operator<=> spaceship operator |
|
// See[p0515 paper](https://wg21.link/p0515r3) |
|
[[nodiscard]] std::strong_ordering operator<=>(const T& rhs) const { |
|
TRACE(std::format("rhs:({})", rhs.id)); |
|
#if 01 |
|
if (a == b) |
|
return std::strong_ordering::equivalent; |
|
if (a < b) |
|
return std::strong_ordering::less; |
|
//if (b < a) |
|
return std::strong_ordering::greater; |
|
#else |
|
if (a == b) |
|
return std::partial_ordering::equivalent; |
|
if (a < b) |
|
return std::partial_ordering::less; |
|
if (b < a) |
|
return std::partial_ordering::greater; |
|
return std::partial_ordering::unordered; |
|
#endif |
|
} |
|
// implicitly declares: [[nodiscard]] bool operator==(const T&) const; |
|
#endif |
|
|
|
// Other |
|
// operator-> member access |
|
T * operator->() { |
|
TRACE(); |
|
return this; |
|
} |
|
// member function -> T const * operator->() const also T * is normal |
|
|
|
// operator* dereference |
|
T & operator*() { |
|
TRACE(); |
|
return *this; |
|
} |
|
// member function -> T const & operator*() const |
|
|
|
// |
|
// Feature-test macro Value Std Feature |
|
// __cpp_static_call_operator 202207L (C++23) static operator() |
|
// __cpp_multidimensional_subscript 202211L (C++23) static operator[] |
|
// |
|
|
|
// operator[] subscript |
|
// pos can be any single argument type |
|
T & operator[](size_t pos) { |
|
TRACE(std::format("pos:{}", pos)); |
|
if (pos == 0) |
|
return *this; |
|
else |
|
return *this; |
|
} |
|
// pos can be any single argument type |
|
T const & operator[](size_t pos) const { |
|
TRACE(std::format("pos:{}", pos)); |
|
if (pos == 0) |
|
return *this; |
|
else |
|
return *this; |
|
} |
|
|
|
// operator->* |
|
// free function -> Anything operator->(Anything lhs, Anything rhs) |
|
|
|
// operator() function |
|
M operator()(M arg) { |
|
TRACE(std::format("arg1:{}", arg)); |
|
return a; |
|
} |
|
|
|
// operator, comma -- Please don't :) |
|
T operator,(M x) { |
|
TRACE(); |
|
return *this; |
|
} |
|
// free function -> Anything operator,(Anything lhs, Anything rhs) |
|
|
|
// operator& address of -- Please don't :) |
|
T * operator&() { |
|
TRACE(); |
|
return this; |
|
} |
|
// member function -> T const * operator&() const or T* |
|
|
|
// operator( ) cast |
|
template<class U> |
|
operator U() const { |
|
TRACE(); |
|
return static_cast<U>(a); |
|
} |
|
|
|
// operator<< stream insertion |
|
// free function -> std::ostream & operator<<(std::ostream & os, T const & value) |
|
|
|
// operator>> stream extraction |
|
// free function -> std::istream & operator>>(std::istream & is, T & value) |
|
|
|
// operator new or operator new[] |
|
// See[cppreference.com operator new](https://en.cppreference.com/w/cpp/memory/new/operator_new) |
|
|
|
// operator delete or operator delete[] |
|
// See[cppreference.com operator delete](https://en.cppreference.com/w/cpp/memory/new/operator_delete) |
|
|
|
#undef TRACE_THIS_ID |
|
#define TRACE_THIS_ID() value.id |
|
|
|
|
|
// operator<< stream insertion |
|
friend std::ostream & operator<<(std::ostream & os, T const & value) { |
|
os << value.a; |
|
TRACE("(ostream insertion)"); |
|
return os; |
|
} |
|
|
|
// operator>> stream extraction |
|
friend std::istream & operator>>(std::istream & is, T & value) { |
|
TRACE("(istream extraction)"); |
|
value.a = {}; |
|
return is; |
|
} |
|
|
|
|
|
#ifdef EXTRA_OVERLOADS |
|
|
|
#undef TRACE_THIS_ID |
|
#define TRACE_THIS_ID() "---" |
|
|
|
// operator&& or operator and logical and |
|
friend bool operator&&(bool lhs, T const & rhs) { |
|
TRACE("(special EXTRA bool && T overload)", std::format("rhs:({})", rhs.id)); |
|
return false; // despite this result, there won't be any 'shortcircuit evaluation'! |
|
} |
|
|
|
// operator|| or operator or logical or |
|
friend bool operator||(bool lhs, T const & rhs) { |
|
TRACE("(special EXTRA bool && T overload)", std::format("rhs:({})", rhs.id)); |
|
return true; // despite this result, there won't be any 'shortcircuit evaluation'! |
|
} |
|
|
|
#endif |
|
|
|
}; |
|
|
|
|
|
|
|
#ifdef SPACESHIP_OPERATOR_IS_SUPPORTED |
|
|
|
static std::string ordering_as_string(const std::strong_ordering o) { |
|
if (o == std::strong_ordering::less) |
|
return "less"; |
|
if (o == std::strong_ordering::equal) |
|
return "equal"; |
|
if (o == std::strong_ordering::equivalent) |
|
return "equivalent"; |
|
if (o == std::strong_ordering::greater) |
|
return "greater"; |
|
return "???"; |
|
} |
|
|
|
#endif |
|
|
|
|
|
static void report_start_of_test(const char *operation) { |
|
currently_tested_expression = operation; |
|
|
|
std::cout << "\n---------------------------------------\n"; |
|
std::cout << "____________-- expression: " << operation << "\n\n"; |
|
} |
|
|
|
|
|
template <class T> |
|
static void ignore_unused(const T &v, const char *operation) { |
|
(void)(v); |
|
|
|
std::cout << "\nresulting value: " << v; |
|
|
|
std::cout << "\n^^^^^^^^^^^^-- output for: " << operation; |
|
std::cout << "\n---------------------------------------\n\n"; |
|
|
|
currently_tested_expression = nullptr; |
|
} |
|
|
|
#define ignore_unused(arg) report_start_of_test(#arg); ignore_unused(arg, #arg); |
|
|
|
static void T_demo(void) { |
|
T<int> t1, t2, t3, t4, t5, t6; |
|
T<std::string> s1, s2, s3; |
|
|
|
#ifdef STD_FORMAT_IS_SUPPORTED |
|
std::cout << std::format("{}/{} / {}/{}", t1.id, t2.id, s1.id, s2.id); |
|
#else |
|
std::cout << t1.id << "/" << t2.id << " / " << s1.id << "/" << s2.id; |
|
#endif |
|
|
|
std::cout |
|
<< '\n' |
|
<< std::boolalpha |
|
<< (t1 == t2) << ' ' |
|
<< (t1 != t2) << ' ' |
|
<< (t1 < t2) << ' ' |
|
<< (t1 <= t2) << ' ' |
|
<< (t1 > t2) << ' ' |
|
<< (t1 >= t2) << ' ' |
|
#ifdef SPACESHIP_OPERATOR_IS_SUPPORTED |
|
<< ordering_as_string(t1 <=> t2) |
|
#endif |
|
<< '\n'; |
|
|
|
std::cout << "---------------------------------------\n\n"; |
|
|
|
// any of the following operators: + - * / % ^ & | ~ ! = < > += -= *= /= %= ^= &= |= << >> >>= <<= == != <= >= <=>(since C++20) && || ++ -- , ->* -> () [] |
|
ignore_unused(t1 + t2); |
|
ignore_unused(t1 - t2); |
|
ignore_unused(t1 * t2); |
|
ignore_unused(t1 / t2); |
|
ignore_unused(t1 % t2); |
|
ignore_unused(t1 ^ t2); |
|
ignore_unused(t1 & t2); |
|
ignore_unused(t1 | t2); |
|
ignore_unused(~t1); |
|
ignore_unused(!t1); |
|
ignore_unused(t1 < t2); |
|
ignore_unused(t1 <= t2); |
|
ignore_unused(t1 == t2); |
|
ignore_unused(t1 != t2); |
|
ignore_unused(t1 >= t2); |
|
ignore_unused(t1 > t2); |
|
#ifdef SPACESHIP_OPERATOR_IS_SUPPORTED |
|
ignore_unused(ordering_as_string(t1 <=> t2)); |
|
#endif |
|
ignore_unused(t1 += t2); |
|
ignore_unused(t1 -= t2); |
|
ignore_unused(t1 *= t2); |
|
ignore_unused(t1 /= t2); |
|
ignore_unused(t1 %= t2); |
|
ignore_unused(t1 ^= t2); |
|
ignore_unused(t1 &= t2); |
|
ignore_unused(t1 |= t2); |
|
ignore_unused(t1 << t2); |
|
ignore_unused(t1 >> t2); |
|
ignore_unused(t1 <<= t2); |
|
ignore_unused(t1 >>= t2); |
|
ignore_unused(t1++); |
|
ignore_unused(++t1); |
|
ignore_unused(t1--); |
|
ignore_unused(--t1); |
|
|
|
ignore_unused(t1 && t2 && t3 && t4 && t5 && t6); |
|
ignore_unused(t1 || t2 || t3 || t4 || t5 || t6); |
|
// while the `operator &&` overload documentation says you LOOSE the 'shortcircuit boolean evaluation' of these operators, |
|
// our test output will APPARENTLY show a different reality: only the first `t1 && t2` is invoked, and ditto for the `||` |
|
// so that SOUNDS like shortcircuit evaluation, doesn't it? |
|
// |
|
// Well, acktshuallyy... it ISN'T. |
|
// What happens is this: |
|
// t1 && t2 && t3 && t4 && t5 && t6 |
|
// evaluates to |
|
// operator||(T:t1, T:t2) --> bool, ... |
|
// so the subsequent `&&` in that expression is not about two T object, but a BOOL and a T |
|
// and THAT particular operator&& has NOT been overloaded, so we get shortcircuit for the remainder: |
|
// (bool) && t3 && t4 && t5 && t6 |
|
// because we did not override that particular operator. |
|
// |
|
// Oh, wait... --> #define EXTRA_OVERLOADS 1 |
|
// |
|
// OMG! as you might expect from the C++ spec, now all of them `&&` and `||` in there are evaluated! |
|
// |
|
// Side note: |
|
// This, of course, how brittle that prticular operator overload is, because all it takes |
|
// is writing further complicated/compound `&&` / `||` expressions, where other types/functions |
|
// will be involved and then, suddently and possibly quite unintuitively, some of those boolean comparisons |
|
// are NOT dealt with operator overloads (because there isn't one for that particular combination of |
|
// argument types) and suddenly you're looking at WTF behaviour... That's why it's very much frowned |
|
// upon to overload `&&` and `||`. |
|
// |
|
|
|
ignore_unused(t1[0]); |
|
ignore_unused(t1(1)); |
|
ignore_unused(t1->a); |
|
ignore_unused(&t1); |
|
ignore_unused((t1, t2)); |
|
ignore_unused(long(t1)); |
|
|
|
ignore_unused(t3 = t1); |
|
} |
|
|
|
int main(void) { |
|
std::cout << "Compiler: "; |
|
report_compiler_version(); |
|
std::cout << "Language: "; |
|
report_cpp_language_version(); |
|
std::cout << "+ operator <=> (spaceship) supported? --> " |
|
#ifdef SPACESHIP_OPERATOR_IS_SUPPORTED |
|
<< "YES" |
|
#else |
|
<< "NO" |
|
#endif |
|
<< "\n"; |
|
|
|
std::cout << "+ std::format supported? ---------------> " |
|
#ifdef STD_FORMAT_IS_SUPPORTED |
|
<< "YES" |
|
#else |
|
<< "NO" |
|
#endif |
|
<< "\n"; |
|
|
|
std::cout << "---------------------------------------\n\n"; |
|
|
|
T_demo(); |
|
|
|
std::cout << "\n\nDone!\n\n"; |
|
|
|
return 0; |
|
} |