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.
精彩评论