0

I have a class-template representing a mathematical vector:

template<class Value_T, unsigned int N>
class VectorT
{
public:

    void Normalize()
    {
        // normalize only double/float vectors here
    }

private:
    // elements in the vector
    value_type elements[N];     

    // the number of elements
    static const size_type size = N;
};

I would like to have a special treatment for vectors of integer types, as a vector normalization is not possible on this types. So I need a seperate (may be specialization) for the Normalize method that depends on the template argument Value_T of the VectorT class-template.

I have tried to use template specialization in different ways but did not get it to work. Do I have to make the Normalize function a template function itself? At the moment it just a normal member-method.

Michbeckable
  • 1,851
  • 1
  • 28
  • 41

4 Answers4

3

You can solve this with a tag dispatching technique:

#include <iostream>
#include <type_traits>

template<class Value_T, unsigned int N>
class VectorT
{
public:
    void Normalize()
    {
        using tag = std::integral_constant<bool
                                         , std::is_same<Value_T, double>::value
                                           || std::is_same<Value_T, float>::value>;  

        // normalize only double/float vectors here
        Normalize(tag());
    }

private:
    void Normalize(std::true_type)
    {
        std::cout << "Normalizing" << std::endl;
    }

    void Normalize(std::false_type)
    {
        std::cout << "Not normalizing" << std::endl;
    }

    // elements in the vector
    Value_T elements[N];     

    // the number of elements
    static const std::size_t size = N;
};

DEMO

Piotr Skotnicki
  • 46,953
  • 7
  • 118
  • 160
  • Interesting approach. I had to change the syntax and remove the using directive (compiler complains) and put the std::integral_constant directly as argument to the Normalize method. The () operator is essential here, but I do not understand how it works here? – Michbeckable Oct 08 '14 at 20:36
  • @Michbeckable: As an alternative to `using` you can use the old `typedef` syntax like `typedef std::integral_constant<..stuff..> tag;`. The parens instantiate an instance of that type, which correctly dispatches the call to appriopriate `Normalize` function. It matches, since `true_type` is a typedef for `integral_constant` and `false_type` is a typedef for `integral_constant`. no magic at all. – Piotr Skotnicki Oct 08 '14 at 21:03
  • I see, the () do an instantiation, as that before is only creating the type from template. Thats the point for me, thx. – Michbeckable Oct 09 '14 at 09:51
1

It seems that you want forbid Normalize for other type than floating point, so you may use static_assert to have good error message:

template<class Value_T, unsigned int N>
class VectorT
{
public:

    void Normalize()
    {
        static_assert(std::is_floating_point<Value_T>::value, "Normalize available only for floating point");
        // normalize only double/float vectors here
    }

// Other stuff

};
Jarod42
  • 203,559
  • 14
  • 181
  • 302
1

Also you can use std::enable_if<>

#include <iostream>
#include <type_traits>

template<class Value_T>
class VectorT
{
public:
    template<class T = Value_T>
    typename std::enable_if<std::is_integral<T>::value, void>::type
    Normalize()
    {
        std::cout << "Not normalizing" << std::endl;
    }

    template<class T = Value_T>
    typename std::enable_if<!std::is_integral<T>::value, void>::type
    Normalize()
    {
         std::cout << "Normalizing" << std::endl;
    }
};

int main()
{
    VectorT<int> vint;
    VectorT<double> vdouble;

    vint.Normalize();
    vdouble.Normalize();

    return 0;
}

DEMO

Kastaneda
  • 739
  • 1
  • 8
  • 15
  • consider: `vint.Normalize();` which results in `"Normalizing"` – Piotr Skotnicki Oct 08 '14 at 16:50
  • I see no change, if you want to use templated member function and `enable_if` then you should also restrict what the use can put as `Y` in `Normalize`, e.g with `static_assert(is_same::value, "!")` inside the body. but that's too complicated for OP's problem – Piotr Skotnicki Oct 08 '14 at 17:06
  • I had changed DEMO example. Yes, you're right about restrict, i missed it. – Kastaneda Oct 08 '14 at 17:13
1

Yes, you can independently specialize a specific member function of a template class. However, function templates (including member function templates) do not allow partial specializations. Function templates only support explicit specializations. An explicit specialization in your case that would look as follows

// Header file

template<class Value_T, unsigned int N>
class VectorT
{
public:

  void Normalize()
  {
      // normalize only double/float vectors here
  }
  ...
};

// Declare an explicit specialization for <int, 5>
template <> void VectorT<int, 5>::Normalize();

and then

// Implementation file

// Define the explicit specialization for <int, 5>
template <> void VectorT<int, 5>::Normalize()
{
   ...
}

However, explicit initialization is not what you need, apparently, since you want to "fix" the type only and leave the size flexible, i.e. you need a partial specialization. This can be done using the std::enable_if functionality of C++11 (as shown in other answers) as well as through some basic tricks of C++98.

Of course if your class is relatively lightweight, i.e. it does not have much generic code besides that Normalize, you can simply partially specialize the whole class. It will take just a bit more typing.

AnT stands with Russia
  • 312,472
  • 42
  • 525
  • 765
  • Which compiler supports separated declaration/implementation files for templates? – Kastaneda Oct 08 '14 at 17:06
  • 2
    @Kastaned: All compilers I know of do. When you *explicitly specialize* a template, it is no longer a template (it no longer has any parameters). An explicitly specialized template entity obeys the One Definition Rule as the corresponding non-template entity. For example, if you explicitly specialize a template function, you have to declare it in the header file and define it in the implementation file, just like you do with ordinary functions. Failure to follow that rule will result in ODR violation and compile (link) error. – AnT stands with Russia Oct 08 '14 at 17:09
  • The seperation of templates in .h/.cpp files (export keyword) is not given in C++14 anymore. But this example also works within a single header file, I tried it, by leaving out the first declaration. But you are right, the explicit (full) specialization is not what I need so I go with the answer by Piotr. – Michbeckable Oct 08 '14 at 19:20
  • 1
    @Michbeckable: This is not related to `export` keyword. And I don't know what compiler allowed you to define the explicit specialization in the header file. Assuming the header file is included into multiple translation units (of course), all compilers I tried produce a multiple definition error, as they should (since this is an ODR violation). – AnT stands with Russia Oct 08 '14 at 19:26
  • @AndreyT: I use VS2012. It works in the header file! I think this is an exception of ODR since we are dealing with templates. "There can be more than one definition of a [...] non-static function template (14.5.6), [...], member function of a class template (14.5.1.1), or template specialization for which some template parameters are not specified (14.7, 14.5.5) [...]". The above case is a member function of a class template. Linker does not even complain when defining a (non-static) function template in the header, but only when further defining an explicit specialization of that template. – Michbeckable Oct 09 '14 at 09:26
  • 1
    @Michbeckable: Yes, it looks like MSVC does not complain about it. However, this cannot be possible allowed formally. Firstly, GCC always catched such errors very reliably. It this were a bug in GCC, it would have been noticed and reported already. – AnT stands with Russia Oct 09 '14 at 14:23
  • 1
    Secondly, the standard does not allow it. An explicit specialization of a function template is not a "function template". This specialization is not a "member function of class template". And the piece you quoted is very specific about template specializations: some template parameters have to be *unspecified*. This is the key moment here. In out case all parameter are specified, which means that having multiple definitions of this specialization is not allowed. – AnT stands with Russia Oct 09 '14 at 14:24
  • The additional formal clarifications are given in 14.7/5: "No program shall explicitly instantiate any template more than once, both explicitly instantiate and explicitly specialize a template, or **specialize a template more than once for a given set of template-arguments**. An implementation is not required to diagnose a violation of this rule." So, both GCC and MSVC are formally correct here, MSVC simply taking advantage of "implementation is not required to diagnose" permission. However, I do remember seeing this error reported by MSVC as well. But a simple example does not reproduce it. – AnT stands with Russia Oct 09 '14 at 14:33
  • I have further investigated this and made a new question for that issue: http://stackoverflow.com/questions/26305368/c-define-member-function-outside-template-class-but-in-header Join the discussion :) – Michbeckable Oct 10 '14 at 18:00