I would expect the code below to fail to compile because of the member function f
of Exclamator<B>
as B
does not provide required name
member. In reality it compiles fine when f
is not used, even with optimizations turned off. How is this possible?
#include <string>
#include <iostream>
class A {
public:
std::string name() { return "A"; }
};
class B {} ;
template <typename T>
class Exclamator {
public:
Exclamator(T a): x{a} {}
void f() {
std::cout << x.name() << std::endl;
}
private:
T x;
};
int main() {
A a;
Exclamator xa {a};
xa.f();
B b;
Exclamator xb {b};
// xb.f();
return 0;
}
$ g++ -std=c++17 -O0 main.cpp
$ ./a.out
A
>Solution :
It’s possible because the C++ standard actually requires this behavior. A method of a template class is only instantiated when it is used. You can declare an instance of this template class, but until something references a specific method, it does not really "exist". The template method must still have valid grammar, but until it’s instantiated it exists in a sort of a limbo state. This allows a template class to contain methods that are well-formed only when the template is instantiated with particular classes. Most of the time it’s necessary to specialize a template class in order to get additional methods or lose others. But, sometimes, it’s possible to get away with it, without specialization, with this approach.
This is crucial and fundamental to C++. If you browse a good C++ reference site, you will note that many constructors and other template class methods are annotated that they "participate in overload resolution" only when certain, specific conditions are met. This is (a part of) what makes it all possible.