简体   繁体   中英

Template specialization only for certain methods

I can't specialize some methods in my vec2 template class. Here's my code:

#pragma once

template<typename Number>
struct vec2
{
    static_assert(std::is_same<Number, int>::value
               || std::is_same<Number, float>::value
               || std::is_same<Number, double>::value,
               "Type not allowed. Use <int>, <float> or <double>.");
    Number x, y;

    vec2();
    vec2(Number x, Number y);

    void add(const vec2& other);
    inline Number lengthSquared() const;
    /*Some other general methods like this two*/
}

My problem is: i want to specialize my length method this way:
it must return a float if template type is int ( vec2<int> )
it must return a float if template type is float ( vec2<float> )
it must return a double if template type is double ( vec2<double> )

I previously specialized my length method like this:

struct vec2
{
/* ... */

inline Number length() const;
}
/*Outside vec2 struct, but in vec2.h*/
template<> inline int vec2<int>::length() const;
template<> inline float vec2<float>::length() const;
template<> inline double vec2<double>::length() const;

and then implemented that in my .cpp file. And that works fine but it can only return the same template type, it can't return a float length for a vec2<int> . Is there a way to do that?

You can write a helper type which gives you the return type for length given the vector component type.

template<typename T>
struct vec_length_t {};

// Specializations:
template<>
struct vec_length_t<int> { using type = float; };

template<>
struct vec_length_t<float> { using type = float; };

template<>
struct vec_length_t<double> { using type = double; };

(or give it a more general name to be reused somewhere else, like floatify or similar)

Then use it like this:

template<typename Number>
struct vec2 {
    ...
    typename vec_length_t<Number>::type length() const;
    ...
};

To be reused for multiple functions or uses in the same class, you can of course also use a local type alias:

template<typename Number>
struct vec2 {
    ...
    using length_t = typename vec_length_t<Number>::type;
    ...
    length_t length() const;
    ...
};

This makes it easy to also use length_t in the function body to call the correct std::sqrt overload (you probably don't want to use the double overload when you are about to return a float !):

template<typename Number>
vec2<Number>::length_t vec2<Number>::length() const {
    // Note that x*x+y*y is a Number, but we want a length_t:
    return std::sqrt(static_cast<length_t>(x*x + y*y));
    // Or, if you have lengthSquared() defined as returning a Number:
    return std::sqrt(static_cast<length_t>(lengthSquared()));
}

There are several possible options:

1) Use some form of helper traits template to provide the return type for length:

  • if only specializations for the types you want to store in the template (int, float, double) are provided, then you could get rid of static_assert (although the static assert most likely has advantage when it comes to the readability of the potential error message)

  • one could also provide specialization of this traits template only for int and provide generic version for everything else, combined with the static_assert already there would work the same but with two less specializations to write, but in my opinion the knowledge about the types handled by your class would then be spread in two places (traits and static assert)

2) In case the implementation for the 'general' case (double and float) is the same and only int case is different - one could also make public length call private length overloads taking false_type/true_type with the result of is_same :), less expandable but without the need for any external types:

auto length() -> decltype(length(is_same<T, int>) { return length(is_same<T, int>); }
Number length(std::false_type) { .... }
float length(std::true_type) {....}

Of course with C++14 one could get rid of decltype mambo-jambo.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM