As for your (original) title
Can a C++ template be typechecked without instantiating it?
Depends on what exactly is meant with typecheck.
does the language standard guarantee to detect the error at the time the template is defined?
Regarding the template declaration (!) and definition itself, it will be checked for syntactical correctness before instantiation, as you mentioned in your question.
There is some type checking done ...
template<typename T>
class Foo {
Foo() : x(y) {}
private:
int &x;
T z;
};
int main() {
}
clang =============
main.cpp:4:20: error: use of undeclared identifier 'y'
Foo() : x(y) {}
^
1 error generated.
gcc =============
main.cpp: In constructor 'Foo<T>::Foo()':
main.cpp:4:20: error: 'y' was not declared in this scope
Foo() : x(y) {}
^
See the Demo
... but not consistently with
template<typename T>
class Foo {
Foo() : x(Foo::y) {}
private:
int &x;
T z;
};
int main() {
}
clang =============
main.cpp:4:25: error: no member named 'y' in 'Foo<T>'
Foo() : x(Foo::y) {}
~~~~~^
1 error generated.
gcc =============
Demo
Where GCC also throws an error, when Foo
is actually instantiated:
template<typename T>
class Foo {
public:
Foo() : x(Foo::y) {}
private:
int &x;
T z;
};
int main() {
Foo<int> foo; // <<<<<<<<<<<<<<<<<<<<<
}
clang =============
main.cpp:5:25: error: no member named 'y' in 'Foo<T>'
Foo() : x(Foo::y) {}
~~~~~^
1 error generated.
gcc =============
main.cpp: In instantiation of 'Foo<T>::Foo() [with T = int]':
main.cpp:12:18: required from here
main.cpp:5:26: error: 'y' is not a member of 'Foo<int>'
Foo() : x(Foo::y) {}
^
Demo
Or is the error guaranteed to be detected only when the template is instantiated?
So this seems to be true.
How far that goes seems to be a compiler implementation specific detail.
So no, obviously there are no standard guarantees regarding that.
Also as @Jarod42 has shown in their Clang/GCC sample
template <typename T>
void foo()
{
int a = "hello world";
const char* hello = 42;
}
int main()
{
}
clang =============
main.cpp:6:9: error: cannot initialize a variable of type 'int' with an lvalue of type 'const char [12]'
int a = "hello world";
^ ~~~~~~~~~~~~~
main.cpp:7:17: error: cannot initialize a variable of type 'const char *' with an rvalue of type 'int'
const char* hello = 42;
^ ~~
2 errors generated.
gcc =============
So I'm afraid there's nothing more available than can be found in section §14.5 of the c++ standard specification what is considered valid template declaration/definition syntax.
Regarding former versions of your question:
I'd like to know how much type checking can be done with a template definition, before the template is instantiated.
The compiler needs to see the concrete parameter types (and non type parameter values) to apply (and in turn instantiate) the constraint checking templates for these types.
So the template must be instatiated to do this.
- Perhaps a template is a lot like a macro in Lisp: the compiler checks the syntax, but no typechecking is done until the template is instantiated. And each time the template is instantiated, the compiler runs the typechecker again.
That seems to come closest, though there's nothing like a typechecker that runs during compilation, but mostly instantiating other template classes and let a std::static_assert
decide in the end, if the constraints for the type (or non type) parameter are met or not.
To see how the c++ standard library deals with that, see Library Concepts please.