Zebrafish
Zebrafish

Reputation: 13886

Is there a way to typedef a type for template function arguments?

The code below should show what I mean by whether you can use a templated function typedef in the arguments...

#include <vector>

struct vec2
{
    float x, y;
};
struct dvec2
{
    double x, y;
};

template <typename T>
void function(std::vector<T>& list, decltype(T::x) scalarVal, decltype(T::x) scalarVal2)
{

    typedef decltype(T::x) scalarType;
    scalarType a; // a is now a double or float depending on template argument
}

int main() 
{
    std::vector<vec2> vecOfVec2;
    std::vector<dvec2> vecOfDvec2;
    function(vecOfVec2, 0.f, 1.f);
    function(vecOfDvec2, 0.0, 1.0);

}

So you can see that in the function I've made a typedef of:

typedef decltype(T::x) scalarType;

And then use scalarType to mean either float or double. I would find it useful if I could list the function arguments of the function as:

void function(std::vector<T>& list, scalarType scalarVal, scalarType scalarVal2)

but seeing as though the typedef isn't made until inside the function this won't work. If it's not possible is this acceptable to do:

(decltype(T::x) scalarVal, ...)

for the arguments each time the way I've shown it in the example?

Upvotes: 4

Views: 240

Answers (2)

Michael Pacheco
Michael Pacheco

Reputation: 1164

I'm facing the same problem in a different scenario. I need to implement * operator for a 2-dim vector of any type and a scalar of any type. This is the implementation using templates:

template <typename T, typename U>
constexpr sf::Vector2<decltype(T{} *U{})> & operator*(sf::Vector2<T>& left, U right) {
    return sf::Vector2<decltype(T{} *U{})> (left.x * right, left.y * right);
}

The problem with that is repeating sf::Vector2<decltype(T{} *U{})> every time. Thinking how to solve this, I dicovered a trick using macros but I'm not sure if there is any side effect.

#define R sf::Vector2<decltype(T{} *U{})>
template <typename T, typename U>
R operator*(const sf::Vector2<T>& left, U right) {
    return R(left.x * right, left.y * right);
}
#undef R

Upvotes: 0

Jodocus
Jodocus

Reputation: 7581

And then use "scalarType" to mean either float or double. I would find it useful if I could list the function arguments of the function as:

This can not work, as the scalar type depends on the vector type. How could you know which type is correct if you ommit the type dependence there? Either way, you have to provide somewhere what vector type you use.

If it's not possible is this acceptable to do:

(decltype(T::x) scalarVal, ...)

for the arguments each time the way I've shown it in the example?

There are better options. This way, you make the interface of a function dependent on the internal data representation, which is not advisory. Implementations may differ, implementations may change, and breaking changes would render the interface invalid this way. Furthermore, for anybody else working with your code not knowing its internals, he/she would have to actually check into the implementation details to find out what you are actually meaning. Instead, you could just define a common name inside each vector, i.e.

struct dvec {
    using scalarType = double;
    ...
};

struct vec2 {
    using scalarType = float;
    ...
};

template <typename T>
void foo(typename T::scalarType bar) { ... }

This is a very common pattern used throughout the STL.

Upvotes: 5

Related Questions