简体   繁体   中英

Define member function of templated class with array argument C++

Consider I have a class template which takes an array as an argument:

template <int N, const float numbers[N]>
class TemplateClass
{
public:
    TemplateClass()
    {
        for (int i = 0; i < N; i++)
        {
            cout << numbers[i] << endl;
        }
    }
};

I can use it successfully like this:

const int N = 3;
extern const float Constants[N];
const float Constants[N] = { 2.0f, 2.1f, 2.2f };

int main()
{
    TemplateClass<3, Constants>();
    return 0;
}

However, my attempts at moving the constructor method body outside of the class declaration were in vain:

// Fails with: 
// error C2440: 'specialization' : cannot convert from 'const float *' to 'const float [3]'
template <int N, const float numbers[N]>
TemplateClass<N, numbers>::TemplateClass()
{
    for (int i = 0; i < N; i++)
    {
        cout << numbers[i] << endl;
    }
}



// Fails with:
// error C3860: template argument list following class template name must list parameters in the order used in template parameter list
// error C3855: 'TemplateClass<N,numbers>': template parameter 'numbers' is incompatible with the declaration
template <int N, const float* numbers>
TemplateClass<N, numbers>::TemplateClass()
{
    for (int i = 0; i < N; i++)
    {
        cout << numbers[i] << endl;
    }
}

This is observed on both VC++11 and VC++12 compilers. How do I solve this?

Reason/Bug:

According to the standard §14.1/4, non-type template-parameter must have one of the following types:

  1. Integral or enumeration type,
  2. Pointer to object or pointer to function,
  3. lvalue reference to object or lvalue reference to function,
  4. pointer to member,
  5. std::nullptr_t .

const float numbers[N] is none of the above. See also ( https://stackoverflow.com/a/16214727/2352671 )

Now the question that still remains is why when you define the constructor in-line (ie, inside class's definition) your program compiles and runs fine.

The answer to this question is that Non-type template parameters that are declared as arrays or functions are converted to pointers or pointers to functions, respectively. We can confirm this by printing the type of numbers inside the in-lined constructor:

template <int N, const float numbers[N]>
class TemplateClass
{
public:
    TemplateClass()
    {
        std::cout << typeid(numbers).name() << std::endl;
        for (int i = 0; i < N; i++)
        {
            std::cout << numbers[i] << std::endl;
        }
    }
};

The output we are getting is:

float const *

As such, the standards aren't being violated and the code works.

The next question we have to answer is why when you define the constructor outside class's definition with the template list of parameters as template <int N, const float *numbers> you get a compiler error.

The answer to this question is because the list of parameters in you class definition (ie, template <int N, const float numbers[N]> ) and the list of parameters in your construction's definition do not match.

Solution:

#include <iostream>

using namespace std;

template <int N, const float* numbers>
class TemplateClass
{
public:
    TemplateClass();
};

template <int N, const float *numbers>
TemplateClass<N, numbers>::TemplateClass()
{
    for (int i = 0; i < N; i++)
    {
        cout << numbers[i] << endl;
    }
}

const int N = 3;
extern const float Constants[N];
const float Constants[3] = { 2.0f, 2.1f, 2.2f };

int main()
{
    TemplateClass<3, Constants>();
    return 0;
}

Output:

2

2.1

2.2

the template methods must be defined in the same file header of the template class. A template is a declaration only, It is instantiated by compiler when you use it for your variable, so the template methods cannot be implemented in a cpp file.

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