Reputation: 18188
Please consider the following code snippet:
template<class E>
class vector_expression {};
template<class Tuple>
class vector
: public vector_expression<vector<Tuple>>
{
public:
using value_type = typename Tuple::value_type;
using size_type = typename Tuple::size_type;
};
namespace detail
{
template<class E>
constexpr bool is_vector_expression_v = std::is_base_of_v<vector_expression<std::decay_t<E>>, std::decay_t<E>>;
template<class E, class = void>
struct value_type { using type = std::decay_t<E>; };
template<class E>
struct value_type<E, std::enable_if_t<is_vector_expression_v<E>>> { using type = typename std::decay_t<E>::value_type; };
template<class E>
using value_type_t = typename value_type<E>::type;
template<class E1, class E2, class BinaryOperation>
class vector_binary_operation
{
public:
using value_type = std::result_of_t<BinaryOperation(value_type_t<E1>, value_type_t<E2>)>;
};
}
E1
and E2
might be either a type derived from vector_expression
or any type used as the value_type
of a vector
.
The vector
type corresponding to the result of a vector_binary_operation
needs to be chosen with care. It makes perfectly sense that its value_type
equals the type of the result of the invoked BinaryOperation
.
However, the correct size_type
is harder to deduce. Here's what I want to do:
E1
and E2
are vector_expression
s, then let size_type = std::conditional_t<
std::numeric_limits<typename std::decay_t<E1>::size_type>::max() <
std::numeric_limits<typename std::decay_t<E2>::size_type>::max(),
typename std::decay_t<E1>::size_type,
typename std::decay_t<E2>::size_type>>>;
E1
or E2
(say E1
) is not a vector_expression
, then let size_type = typename std::decay_t<E2>::size_type
It won't happen that E1
and E2
are both no vector_expression
s. However, (take that as a secondary question) should I add a class = std::enable_if_t<is_vector_expression_v<E1> || is_vector_expression_v<E2>>
to the template parameter list of vector_binary_operation
?
The main question is: How can I define size_type
as described above?
Upvotes: 1
Views: 82
Reputation: 179917
Generally I find that in such cases, it may often be easiest to just define a set of overloads auto Foo(E1,E2) -> ResultType
to do the actual operation, and then set vector_binary_operation::size_type = decltype(Foo(E1,E2))
.
In your particular case, you'd have three overloads: for E1, E2 or both being a vector_expression.
In this particular case I see an alternative. Introduce a helper template<typename T> class sizer { const int max = 0; }
with a specialization template<typename T> class sizer<vector_expression<T>> { const int max = std::numeric_limits<typename std::decay_t<E1>::size_type>::max() ; }
.
The idea behind this is simple. Your general expression compares two properties from two vector expressions, and picks the type for which the property is greatest. By defining this property to be zero for all other types, all other types rank below your vector_typs and won't get chosen.
Upvotes: 1
Reputation: 217478
A way to do that is with additional traits or enrich your current traits:
You may make a traits which give sizeof(E1::size_type)
for vector_expression
and 0
for
template<class E, class = void>
struct value_type {
using type = std::decay_t<E>;
using size_type = char; // or better some dummy type
static constexpr std::size_t size = 0; // To be the minimal size
};
template<class E>
struct value_type<E, std::enable_if_t<is_vector_expression_v<E>>> {
using type = typename std::decay_t<E>::value_type;
using size_type = typename std::decay_t<E>::size_type;
static constexpr std::size_t size = std::numeric_limits<size_type>::max();
};
And then
template<class E1, class E2, class BinaryOperation>
class vector_binary_operation
{
public:
using value_type = std::result_of_t<BinaryOperation(value_type_t<E1>,
value_type_t<E2>)>;
using size_type = std::conditional_t<(value_type<E1>::size < value_type<E2>::size),
typename value_type <E1>::size_type,
typename value_type <E2>::size_type>>>;
};
Upvotes: 2