a concerned citizen
a concerned citizen

Reputation: 839

Assign value based on type comparison

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

Answers (4)

ovanes
ovanes

Reputation: 5673

I would just define in the class two static helper functions:

  • double as parameter
  • catch all template version, which calls toDouble() member

if 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

YePhIcK
YePhIcK

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:

  1. Derive from it. Have your own class that does the conversion from the base.

Something like that:

class myDouble : public mpfr::mpreal{
    public:
        myDouble() : mpfr::mpreal() {}
        operator double() const {return this->toDouble();}
};
  1. Another option is to use templatized functions and specialize them.

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

skyking
skyking

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

molbdnilo
molbdnilo

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

Related Questions