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?

7
  • 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. Commented May 9, 2014 at 20:52
  • Brian, you're correct, that's why I tried using "template <int N, const float* numbers>". 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. Commented May 9, 2014 at 21:03
  • I can reproduce this error in VS2013 too. Commented May 9, 2014 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*. Commented May 9, 2014 at 21:19
  • Yea, but then I lose the ability enforce array size to actually match N... Commented May 9, 2014 at 21:28

2 Answers 2

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

Sign up to request clarification or add additional context in comments.

7 Comments

No, I moved it out. And your solution doesn't compile for me. Which compiler are you using?
I wonder if it's a bug in Microsoft compiler then, because it fails to compile in both VS 2012 and VS 2013.
I test on VC2013 wait a bit.
I can reproduce in VS2013.
Yeah me too, what the ... :s
|
-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.

2 Comments

I'm not trying to define it in another file, just outside class declaration.
This is incorrect. See stackoverflow.com/questions/115703/… for example

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.