1

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?

Sunius
  • 2,789
  • 18
  • 30
  • Side note: The standard says that array template parameters are adjusted to pointers, so `const float numbers[N]` should be processed as though it's `const float* numbers`. – Brian Bi May 09 '14 at 20:52
  • Brian, you're correct, that's why I tried using "template ". However, it then fails compilation saying declaration doesn't match the definition. And no, I didn't move it to classTemplate.cpp. Everything is in a single file. – Sunius May 09 '14 at 21:03
  • I can reproduce this error in VS2013 too. – dlf May 09 '14 at 21:07
  • It's not as robust since a size mismatch is possible, but it will work if you replace all the `const float[N]`s in the template with `const float*`. – dlf May 09 '14 at 21:19
  • Yea, but then I lose the ability enforce array size to actually match N... – Sunius May 09 '14 at 21:28
  • @Sunius it already does not enforce array size to match N ; if you want to do that then you should take the array by reference (which would also solve your redefinition problem) – M.M May 09 '14 at 23:10
  • Actually, it does enforce it - making N not match array size does not compile on neither VS 2012 nor VS 2013. – Sunius May 10 '14 at 12:47

2 Answers2

2

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 (i.e., 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 (i.e., 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

Community
  • 1
  • 1
101010
  • 41,839
  • 11
  • 94
  • 168
-1

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.

AngeloDM
  • 397
  • 1
  • 8