Given a simple template <typename T> struct X { T x, y; };
, I want to provide conversion constructors such that user can write:
X<double> a;
X<int16_t> b = a; // uses implicit conversion ctr (compiles with warning)
X<int16_t> c(a); // uses explicit conversion ctr (compiles w/o warning)
X<int32_t> d = c; // uses implicit conversion ctr (compiles w/o warning)
I believe that to achieve this goal, I need to implement both, an implicit and an explicit conversion constructor from type U
. But it's not possible to overload on "implicit" and explicit
:
template <typename T> struct X {
X(T x = T(), T y = T()) : x(x), y(y) {}
// implicit conversion
template <typename U>
X(const X<U>& other) : x(other.x), y(other.y) {}
// not possible!
template <typename U>
explicit X(const X<U>& other)
: x(static_cast<T>(other.x))
, y(static_cast<T>(other.y))
{}
T x, y;
};
How could I achieve this goal (I guess I can't...)?
My initial thought was that I need to enable/disable the one or other depending on is_lossless_convertible
. So is there a suitable type trait?
I would like to test if a scalar type U
is convertible to type T
without loss of precision:
using namespace std;
static_assert(is_lossless_convertible<int16_t, int32_t>::value == true);
static_assert(is_lossless_convertible<int32_t, int16_t>::value == false);
static_assert(is_lossless_convertible<int16_t, uint32_t>::value == false);
static_assert(is_lossless_convertible<int32_t, double>::value == true);
static_assert(is_lossless_convertible<double, int32_t>::value == false);
In short, it should yield true
if std::is_convertible<U, T>::value == true
and if U x; T y = x;
would not issue a compiler warning regarding loss of information.
You can't overload on explicitness, but you can provide an explicit converting constructor and implicit conversion operator:
#include <iostream>
template<typename T> struct S {
S() {}
template<typename U> explicit S(const S<U> &) { std::cout << "explicit\n"; }
template<typename U> operator S<U>() { return S<U>(this); }
private:
template<typename U> friend struct S;
template<typename U> S(const S<U> *) { std::cout << "implicit\n"; }
};
int main() {
S<double> sd;
S<int> si1(sd);
S<int> si2 = sd;
}
Output:
explicit
implicit
Tested with gcc and clang.
The explicit converting constructor is preferred to the implicit conversion operator because in the latter case there is an additional (possibly elided) call to the copy constructor of S<int>
.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With