Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Avoid excessive repetition with templated type

Tags:

c++

templates

I want to define a generic function whose arguments and the return type are identical instantiations of a template. This leads to an overly verbose definition. Is there a way to use a shorthand without polluting the enclosing namespace?

Example,

template<class CoordinateType, class ValueType>
struct PointWithValue {
    CoordinateType x, y;
    ValueType value;
}

template<class CoordinateType, class ValueType>
PointWithValue<CoordinateType, ValueType> interpolate(
    PointWithValue<CoordinateType, ValueType> point1,
    PointWithValue<CoordinateType, ValueType> point2)
{
    ...
}

One solution I can come up with is

template<class PointWithValueType>
PointWithValueType interpolate(
    PointWithValueType point1, PointWithValueType point2)

But I am not really happy with this as it obfuscates what I expect as PointWithValueType; it is only implicitly shown inside the body function. And if the caller passes a wrong argument, the error is unlikely to be clear and concise.

I would like something that looks like this

template<class CoordinateType, class ValueType>
using PointWithValueType = PointWithValue<CoordinateType, ValueType>;
PointWithValueType interpolate(
    PointWithValueType point1, PointWithValueType point2)

As far as I can tell the above only works if I wrap it in a class and define the method as static. It kind of works but it also changes the interface (puts the function inside a deeper named scope) and it relies on a class without members and with only a single static function which feels awkward and might confuse the user.

This is a general question, workarounds for this particular problem that do not apply to this class of problem are not suitable answers. Is there something similar to my using example without drawbacks?

like image 324
patatahooligan Avatar asked Aug 26 '19 14:08

patatahooligan


2 Answers

With traits and SFINAE, you might do

template <typename T>
struct IsPointWithValue : std::false_type {};

template <class CoordinateType, class ValueType>
struct IsPointWithValue<PointWithValue<CoordinateType, ValueType>> : std::true_type
{
// Possibly aliases to retrieve template parameters.
};

template<class T, std::enable_if_t<IsPointWithValue<T>::value, int> = 0>
T interpolate(T point1, T point2);
like image 183
Jarod42 Avatar answered Sep 27 '22 02:09

Jarod42


It can be a good idea to switch to using static assert with dedicated type trait (and probably to a concept with C++20) to check that template parameter is of required kind:

template
<
    typename x_MaybePointWithValue
> class
is_point_with_value: public ::std::false_type {};

template
<
    typename CoordinateType
,   typename ValueType
> class
is_point_with_value<PointWithValue<CoordinateType, ValueType>>: public ::std::true_type {};

template
<
    typename x_PointWithValue
> x_PointWithValue
interpolate(x_PointWithValue point1, x_PointWithValue point2)
{
    static_assert
    (
        is_point_with_value<x_PointWithValue>::value
    ,   "template parameter must be an instance of PointWithValue template"
    );
}
like image 25
user7860670 Avatar answered Sep 25 '22 02:09

user7860670