Why do const variables don't need to be initialized in a template class until the class is actually used?

Viewed 82

Why doesn't the compiler throw an error if I don't initialize const variables in an unused template class? If I remove the template keyword the compiler complains as expected.

This code works (unexpected):

#include <iostream>

template<class T>
class Matrix {
private:
    const uint8_t numRows, numColumns;
    T content[];

public:
    Matrix(uint8_t numRows, uint8_t numColumns) 
    // : numRows(numRows), numColumns(numColumns) -- Works fine
    {
        std::cout << "Matrix" << std::endl;
    }
};

int main() {
    // If Matrix constructor is not called the compiler doesn't need a initialization list
    // Matrix<int> matrix(2, 2);
    return 0;
}

But this one does not (expected):

#include <iostream>

template<class T>
class Matrix {
private:
    const uint8_t numRows, numColumns;
    T content[];

public:
    Matrix(uint8_t numRows, uint8_t numColumns)
    // : numRows(numRows), numColumns(numColumns) -- Does NOT work
    {
        std::cout << "Matrix" << std::endl;
    }
};

int main() {
    Matrix<int> matrix(2, 2);
    return 0;
}
2 Answers

Class template isn't implicitly instantiated until required. You don't use Matrix<int>, then it's not instantiated, and definition is not required (to exist or to be well-formed).

When code refers to a template in context that requires a completely defined type, or when the completeness of the type affects the code, and this particular type has not been explicitly instantiated, implicit instantiation occurs. For example, when an object of this type is constructed, but not when a pointer to this type is constructed.

From the standard, [temp.inst]/2:

Unless a class template specialization is a declared specialization, the class template specialization is implicitly instantiated when the specialization is referenced in a context that requires a completely-defined object type or when the completeness of the class type affects the semantics of the program.

[temp.inst]/11:

(emphasis mine)

An implementation shall not implicitly instantiate a function template, a variable template, a member template, a non-virtual member function, a member class or static data member of a templated class, or a substatement of a constexpr if statement ([stmt.if]), unless such instantiation is required.

So in this case, the ill-formed Matrix<T>::Matrix(uint8_t numRows, uint8_t numColumns) is not allowed to be implicitly instantiated.

The code is ill-formed, no diagnostic required (meaning the code is invalid, but compilers are not required to detect this fact).

This is caused by [temp.res.general]/6.4:

The program is ill-formed, no diagnostic required, if:

— a hypothetical instantiation of a template immediately following its definition would be ill-formed due to a construct that does not depend on a template parameter, ...

Related