I have a wrapper class template and want to have implicit conversion to the wrapped type in order to use existing library functions:
#include <complex>
double f(double x) { return 1.; }
template <typename T>
std::complex<T> f(std::complex<T> x) { return std::complex<T>(); }
template <typename T>
class A {
T _x;
public:
A(const T& x) : _x(x) {}
operator T() { return _x; }
};
int main() {
A<double> da(1.);
A<std::complex<double>> ca({1.,1.});
f(da); // OK
f(ca); // error
return 1;
}
The f(std::complex<T>)
is not used for f(ca)
because implicit conversion is not considered during template argument deduction (see the generated error msg. here).
In the real code f(...)
is replaced by library functions e.g. from the <complex>
header, so it can't be modified.
If I inherit A
from T
(as suggested by the error message) then f(ca)
compiles. But then A
is not defined for built-in types (you can't inherit from them). Also, that would give all the functionality of complex<double>
to A<complex<double>>
which I want to avoid.
Is there any workaround for this?
There are three kinds of templates: function templates, class templates and, since C++14, variable templates. Since C++11, templates may be either variadic or non-variadic; in earlier versions of C++ they are always non-variadic.
There are two types of conversion: implicit and explicit. The term for implicit type conversion is coercion. Explicit type conversion in some specific way is known as casting. Explicit type conversion can also be achieved with separately defined conversion routines such as an overloaded object constructor.
In computer science, type conversion, type casting, type coercion, and type juggling are different ways of changing an expression from one data type to another. An example would be the conversion of an integer value into a floating point value or its textual representation as a string, and vice versa.
The C++ language defines conversions between its fundamental types. It also defines conversions for pointer, reference, and pointer-to-member derived types. These conversions are called standard conversions.
To resolve the "doesn't work for built-in types" problem, you can use a template specialization. The std::complex<>
version uses inheritance.
template <typename T>
class A {
T _x;
public:
A(const T& x) : _x(x) {}
operator const T &() const { return _x; }
operator T &() { return _x; }
};
template <typename D>
class A<std::complex<D>> : public std::complex<D> {
typedef std::complex<D> T;
public:
A(const T& x) : T(x) {}
};
If inheritance is not acceptable, the only approach I am aware of is to define functions that take A<>
as an argument. However, you can simplify the task by defining the functions within A<>
itself, and thus leveraging a simplified syntax for the template argument, and argument dependent lookup for function call invocation.
template <typename T>
class A {
T _x;
friend A f(A x) { return f(x._x); }
public:
A(const T& x) : _x(x) {}
operator const T &() const { return _x; }
operator T &() { return _x; }
};
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