开发者

C++ returning more precise of two template arguments from function?

I'm curious if there's any way to do this in C++. Let's say I have a templated vector class:

template <typen开发者_StackOverflow社区ame T>
class vector {          
public:
      vector(T a, T b, T c) : x(a), y(b), z(c) {}

      T x,y,z;
};

And then I have a templated addition operator:

template <typename A, typename B> 
vector<A> operator +(const vector<A> &a, const vector<B> &b) { 
   return vector<A>(a.x+b.x, a.y+b.y, a.z+b.z); 
}

I'm curious if it's possible to modify that operator so the result is whichever of the two types A and B is more precise, aside from manually specializing it.

For example:

vector<float>       + vector<double> would produce a vector<double>, 
vector<long double> + vector<float>  would produce a vector<long double>

My guess would be that there's no automatic support for this in C++, but I thought I'd ask.


There isn't any built-in support in the form of a library, but you can accomplish this using the conditional (?:) operator.

In reply to another answer, Johannes Schaub posted a promote<T, U> template that wraps the logic up quite nicely. With the template, you should be able to write:

template <typename A, typename B>  
vector< typename promote<A, B>::type >
operator+(const vector<A> &a, const vector<B> &b) 
{     
    return vector< typename promote<A, B>::type >(a.x+b.x, a.y+b.y, a.z+b.z);  
} 


in C++0x, you could say:

template <typename A, typename B> 
auto operator +(const vector<A> &a, const vector<B> &b) -> vector<decltype(a.x + b.x)>
{
    //...
}

In C++03, you need to define all the combinations yourself, although you can do it in a reusable op_traits scheme that can be applied to a variety of different operators. James McNellis provides some details on this in his answer


Andrei Alexandrescu discussed this in his 1st April 2001 DDJ article Generic: Min and Max Redivivus.

In short, the general problem is very complex.

Andrei used 80 lines of support code, those lines in turn relying on the Loki library.

Cheers & hth,.


There is a relatively easy way to do this with template specializations

 template< typename A >
 struct TypePrecision {
    static const int precisionLevel;
 };

 template< typename A >
 const int TypePrecision< A >::precisionLevel = 0;


 template<>
 struct TypePrecision< float > {
    static const int precisionLevel;
 };
template<>
 struct TypePrecision< long float > {
    static const int precisionLevel;
 };
  template<>
 struct TypePrecision< double > {
    static const int precisionLevel;
 };
template<>
 struct TypePrecision< long double > {
    static const int precisionLevel;
 };

 template<>
 const int TypePrecision< float >::precisionLevel = 1;
 template<>
 const int TypePrecision< long float >::precisionLevel = 2;
 template<>
 const int TypePrecision< double >::precisionLevel = 3;
 template<>
 const int TypePrecision< long double >::precisionLevel = 4;

Then you use this to create a HigherPrecisionType

 template < typename A , typename B >
 struct HigherPrecisionType
 { 
     static const int APrecision;
     static const int BPrecision;
 };

template < typename A , typename B >
const int HigherPrecisionType< A, B >::APrecision= TypePrecision< A >::precisionLevel;

template < typename A , typename B >
const int HigherPrecisionType< A, B >::BPrecision= TypePrecision< B >::precisionLevel;

I'm not sure how to compare these to get a typedef in the specialization to the appropiate type though. But i hope you get the idea


Pattern "Type Selection" (read about it in "Modern C++ Design") can be useful here.

template <bool flag, typename T, typename U>
struct Select {
    typedef T Result;
};

template <typename T, typename U>
struct Select<false, T, U> {
    typedef U Result;
};

...

template <typename A, typename B> 
vector<Select<sizeof(A) > sizeof(B), A, B>::Result> operator +(const vector<A> &a, const vector<B> &b) { 
   return vector<Select<sizeof(A) > sizeof(B), A, B>::Result>(a.x+b.x, a.y+b.y, a.z+b.z); 
}


I'm choosing the type greater in size:

Helper templates:

template<bool b, typename A, typename B>
struct choose_if
{
    typedef A type;
};    
template<typename A, typename B>
struct choose_if<false, A, B>
{
    typedef B type;
};
template<typename A, typename B>
struct greater 
{
    static const bool value = sizeof(A) > sizeof(B);
    typedef vector<typename choose_if<value, A, B>::type> type;
};

Now use it:

template <typename A, typename B> 
typename greater<A, B>::type operator +(const vector<A> &a, const vector<B> &b) 
{ 
    typedef typename greater<A, B>::type  type;
    return type(a.x+b.x, a.y+b.y, a.z+b.z); 
}

See online demonstration : http://www.ideone.com/PGyA8


You can accomplish your goal somewhat by using function overloading. Meaning that in addition to the generic:

template <typename A, typename B> 
vector<A> operator +(const vector<A> &a, const vector<B> &b) { 
   return vector<A>(a.x+b.x, a.y+b.y, a.z+b.z); 
}

you also declare overloads for specific types, and these then get used rather than generically manufactured ones:

vector<double> operator +(const vector<float> &a, const vector<double> &b) { 
   return vector<double>(a.x+b.x, a.y+b.y, a.z+b.z); 
}

Your other option would be to implement conversion operators on your vector template for the types required. Have a float vector be able to return a double vector via an operator.


Yep. Here's the C++03 method:

template < typename T1, typename T2 >
struct which_return;

template < typename T >
struct which_return<T,T> { typedef std::vector<T> type; };

template <  >
struct which_return<int,double> { typedef std::vector<double> type; };

template < >
struct which_return<double,int> : which_return<int,double> {};

// etc...
template < typename T1, typename T2 >
typename which_return<T1,T2>::type operator+ (std::vector<T1> const&, std::vector<T2> const&)
{
  // ...
}

Obviously you do it the C++0x way if you can.


You'll never be able to accomplish this:

vector<float> + vector<double> would produce a vector<double>

without massive trickery or returning a pointer to some gizmo of your own design because operator+ must return a type that is known at compile-time. You are asking to return a type that is determined at run-time.

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜