Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to provide implicit and explicit conversion ctr for same type?

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.

like image 976
Daniel Gehriger Avatar asked Dec 13 '12 09:12

Daniel Gehriger


1 Answers

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>.

like image 188
ecatmur Avatar answered Oct 16 '22 14:10

ecatmur