Reputation: 839
I am trying to make a template class that accepts, depending on the requirement, either a double
or an mpfr::mpreal
type. This is what I am trying to do:
x = ( typeid(T)==typeid(double) ? value : value.toDouble() );
But it complains at compile time about cannot convert ‘const mpfr::mpreal’ to ‘double’ in assignment. I also tried std::is_same
, with the same errors. Is what I am trying to do forbidden, or simply wrong (or both)? Is there any way I can do this? The calls for the class are different in each file, but fixed, for example in one .cpp
it's called as Class<double>
, in another Class<mpfr::mpreal>
, etc. Here's a quick test program:
#include <iostream>
#include <typeinfo>
#include "mpreal.h"
template<class T>
class Bla
{
private:
double a;
public:
Bla(const T &x) { a = (std::is_same<T, double>::value ? x : x.toDouble()); }
void f() { std::cout << "a = " << a << '\n'; }
};
int main()
{
mpfr::mpreal x {1.0};
double y {2.0};
{
Bla<mpfr::mpreal> a {Bla<mpfr::mpreal>(x)};
a.f();
}
{
Bla<double> a {Bla<double>(y)};
a.f();
}
return 0;
}
Upvotes: 1
Views: 342
Reputation: 5673
I would just define in the class two static helper functions:
toDouble()
memberif x
is not double it'll go to template version.
Here a working example:
#include <iostream>
namespace mpfr
{
struct mpreal
{
double const& toDouble()const
{
return d;
}
double d;
};
}
template<class T>
class Bla
{
double a;
template<class U>
auto to_double(U const& u) -> decltype(u.toDouble())
{
return u.toDouble();
}
static double const& to_double(double const& d) { return d; }
public:
Bla(T const& x)
: a{ to_double(x) }
{}
void f() { std::cout << "a = " << a << '\n'; }
};
With the same main()
implementation as before.
int main()
{
mpfr::mpreal x {1.1};
double y {2.2};
{
Bla<mpfr::mpreal> a {Bla<mpfr::mpreal>(x)};
a.f();
}
{
Bla<double> a {Bla<double>(y)};
a.f();
}
return 0;
}
Note: this approach will also work if you pass float
or int
, with is_same
machinery, it'll fail and you must explicitly catch up all type which can be implicitly converted to double.
Upvotes: 1
Reputation: 5856
This is what cast operators are for. Implement this in your mpfr::mpreal
class and other classes that you want to use in the double
context:
operator double() const;
And your assignment becomes:
Bla(const T &x) { a = x; }
Or, better yet, initializer list:
Bla(const T &x) : a(x) {}
As a side note what you were trying to implement looked more like a conditional compilation, which is usually achieved with #ifdef
and a like. Except it won't work in your particular case as there's no pre-processor construct (that I know of, at least) which would allow you to compare types.
If you can't modify that external class you have two options:
Something like that:
class myDouble : public mpfr::mpreal{
public:
myDouble() : mpfr::mpreal() {}
operator double() const {return this->toDouble();}
};
Something along these lines:
template<typename T> myToDouble(const & T);
template<> double myToDouble(const double & _d) {return _d;}
template<> double myToDouble(const mpfr::mpreal & _d) {return _d.toDouble();}
and then your assignment is looking like that:
Bla(const T &x) : a(myToDouble(x)) {}
Upvotes: 3
Reputation: 14359
A conditional expression must have a single type and if the "true" and "false" expression don't have the same type they will have to be converted. Even if the condition is known at compile time this applies.
What you could have done is for example to define a toDouble
template function that has a specialization for double
:
template <typename T>
double toDouble(T const& x) {
return x.toDouble();
}
double toDouble(double const& x) {
return x;
}
Upvotes: 2
Reputation: 66371
The function needs to be correct regardless of which type T
it's instantiated with.
That is, both
Bla(const double &x) { a = (std::is_same<double, double>::value ? x : x.toDouble()); }
and
Bla(const mpfr::mpreal &x) { a = (std::is_same<mpfr::mpreal, double>::value ? x : x.toDouble()); }
must be correct, and it's pretty clear that neither is – there's no implicit conversion between double
and mpfr::mpreal
, and double
doesn't have a toDouble()
member function.
A simple solution is to delegate the conversion to an overloaded function:
double to_double(double x) { return x; }
double to_double(mpfr::mpreal x) { return x.toDouble(); }
Bla(const T &x)
: a(to_double(x))
{
}
Upvotes: 4