I have small example code:
file foo.h:
#pragma once
template <typename T> class FooNoDef {
public:
void foo(const T& value); // declared and not defined
};
class FooUser {
public:
template <typename T> static void useFoo(const T& value) {
FooNoDef<T>{}.foo(value);
}
};
file xy.h:
#pragma once
struct X {};
struct Y {};
file xy.cpp:
#include "foo.h"
#include "xy.h"
#include <iostream>
template <> void FooNoDef<X>::foo(const X& value) {
std::cout << "x" << std::endl;
}
template <> void FooNoDef<Y>::foo(const Y& value) {
std::cout << "y" << std::endl;
}
and finally main.cpp:
#include "foo.h"
#include "xy.h"
int main() {
FooUser::useFoo(X{});
FooUser::useFoo(Y{});
return 0;
}
This code compiles with gcc 11 and clang 13. I suspect my code is ill-formed, but I can't find a definite answer from reading the standard:
Section 13.9.4 [temp.expl.spec] (emphasis mine):
If a template, a member template or a member of a class template is explicitly specialized, a declaration of that specialization shall be reachable from every use of that specialization that would cause an implicit instantiation to take place, in every translation unit in which such a use occurs; no diagnostic is required. If the program does not provide a definition for an explicit specialization and either the specialization is used in a way that would cause an implicit instantiation to take place or the member is a virtual member function, the program is ill-formed, no diagnostic required. An implicit instantiation is never generated for an explicit specialization that is declared but not defined.
Section 13.9.2 [temp.inst] (emphasis mine):
[Example 5:
template<class T> struct Z {
void f();
void g();
};
void h() {
Z<int> a; // instantiation of class Z<int> required
Z<char>* p; // instantiation of class Z<char> not required
Z<double>* q; // instantiation of class Z<double> not required
a.f(); // instantiation of Z<int>::f() required
p->g(); // instantiation of class Z<char> required, and
// instantiation of Z<char>::g() required
}
Nothing in this example requires class Z, Z::g(), or Z::f() to be implicitly instantiated.** — end example]
I think FooUser::useFoo()
does not cause implicit instantiation of FooNoDef::foo()
as the example from the standard discussed, but still I never provided a declaration for my explicit specialization of FooNoDef<X>
and FooNoDef<Y>
. Which rule of C , if any, do I violate with my example? Would I have to provide explicit specialization declaration template <> void FooNoDef<X>;
and template <> void FooNoDef<Y>;
strictly before the body of FooUser::useFoo()
?
CodePudding user response:
As far as I can see, you are right, though you've put emphasise on the wrong line of the standard:
[...] a declaration of that specialization shall be reachable from every use of that specialization [...]
Within main
, both of FooUser::useFoo<X>
and FooUser::useFoo<Y>
need to be instantiated. These then need to instantiate FooNoDef<X>::foo
and FooNoDef<Y>::foo
– and here an implicit instantiation would be caused, if no explicit instantiation was available.
However, there only exists a definition within xy.cpp
, and that's not visible to main.cpp
, and there's no visible declaration – violating above cited phrase, thus your programme indeed is ill-formed.
To fix, you'd need to add a declaration, e.g. in xy.h
(note: the header that is included by main.cpp
):
template <> void FooNoDef<X>::foo(X const& value);
template <> void FooNoDef<Y>::foo(Y const& value);