This question was at the interview:
Does this code causes any compile/linking errors and why so?
template <int T> void f();
template <> void f<0>() {}
void test()
{
f<1>();
}
Please explain the behaviour. Thanks a lot.
template<> void f<0>() {}
is specialization of function template for argument 0, if you call f<0>() this version of function will be called.
This code is incorrect, it cause linking errors, since there is no specialization for f<1> and template version of function is not defined.
It will compile (all the code is gramatically valid) but will fail at link stage.
This is because template <int T> void f(); is declared but not defined, the <0> specialisation is defined but that makes no odds to you since you're not instantiating it.
Actually, it would be possible for the <0> specialisation to contain syntax errors and the program would still compile without error! This is because formally, templates are only compiled if they are used. (I wouldn't expect a candidate to have the presence of mind during interview conditions to point that out.)
It will compile because compiler can see a declaration for a generic template. There is a fully specialized template for 0 also. But we are calling it for 1, which will try to invoke the generic template, but since linker cannot find any definition for the general template, the program will show linker error.
Soloution
template <int T> void f();
template <> void f<0>() {}
template <int T> void f() { }
void test()
{
f<1>();
}
Related
I would like to prevent the compiler from implicitly instantiating some template with extern template.
The following snippets works as expected (the static_assert does not trigger):
template<typename T>
void f() {
static_assert(sizeof(T) == 0, "f()");
};
extern template void f<int>();
void g() {
f<int>();
}
But with this one it seems that the compiler tries to instantiante the template function, as the static_assert does trigger:
struct S {
template<typename T>
void f() {
static_assert(sizeof(T) == 0, "S::f()");
}
};
extern template void S::f<int>();
void g() {
S s;
s.f<int>();
}
with this one, the static_assert also triggers whereas I would expect that it does not:
template<typename T>
struct S {
S(){};
static_assert(sizeof(T) == 0, "S");
};
extern template struct S<int>;
void g() {
S<int> s;
}
In my actual cases, I would like to speedup compilation times, but I observe that the compilation of translation units where there is an extern template ..., the symbols related to these templates does not appears in the .o file (looking with nm), but they are actually compiled... (I check that through the observation of significant compilation times with clang's -ftime-trace and with templight++).
Why does extern template does not seems to work as expected?
Thanks!
Explicit instantiation declarations can’t suppress all instantiation: a class’s members must still be known to use an object of that type, after all, and inline functions must have their definitions known in order to be inlined.
This example is just both of those in quick succession: the class template can’t “protect” the member function template, and that member function template is inline because it’s defined in its class.
The latter rule is a bit arbitrary: it was once considered necessary for ODR reasons, but the modern understanding is that the “merging” of the whole class (template) definition is sufficient. Accordingly, C++20 removes that implicit inline in modules, where it interacts badly with linkage rules.
I have written this code to understand template name lookup:
//void bar(int);
template <typename T>
void foo(T x)
{
bar(x);
}
void bar(int x)
{
std::cout << "bar(int)\n";
}
template <int>
void foo(int i)
{
bar(i);
}
int main()
{
foo<int>(4);
std::cout << "\ndone!\n";
}
I've commented out the declaration of function bar(int) intentionally. This function bar(int) is used as a dependent name in the template function foo. So it is bound upon instantiation.
I've defined bar right after foo and before a specialization of foo<int> so that this latter can see bar(int).
But when I compile the code I get this error:
‘bar’ was not declared in this scope, and no declarations were found by argument-dependent lookup at the point of instantiation [-fpermissive]|. And if I un-comment the declaration of bar(int) it works fine?!
If I must declare a name before using it in a template as a dependent-name then why C++ allows that if not instantiated. (the code works if I don't "use" templated function foo)?
template <typename U>
void do_it(U u)
{
print(u); // not bound until instantiation
}
So what is the idea behind allowing calling print(u) in do_it which was not already declared and will fail upon instantiation?
In your program, this definition:
template <int> // not a specialization, just a different
void foo(int i) // template with a non-type template parameter
{
bar(i);
}
does not actually define a specialization of the foo primary template. So when you make this call:
Foo<int>(4);
you end up calling the primary template. Lookup for the name bar doesn't find that name, and you get an error.
If instead, you actually write a specialization of foo for int like this:
template <> // specialization of primary
void foo<int>(int i) // template foo with int
{
bar(i);
}
then the call foo<int>(4); is fine, because it calls the specialization, and lookup for bar at that point does find that name.
Now going back to your program (without the specialization), what happens if foo is never instantiated, because there's no call, for example? Well, the program is still wrong, but the compiler may not tell you about it. This is the behavior that you are describing with print and do_it, and this is formally known as ill-formed, no diagnostic required.
Pay attention to the line numbers in that error output.
You’re instantiating the first template. The second template is not a specification of the first but a function template that requires an int template parameter that it doesn’t use.
Consider the following function template declaration:
template <typename T, typename = std::enable_if_t<std::is_same_v<T, int>>>
void foo(T i);
There is only one possible valid instantiation of this template, namely with T = int. I'd like to put this definition in an implementation file. I can think of two possible ways of doing so. (If you're wondering why on earth I'd do this rather than just saying void foo(int i), it's because the template version prevents implicit conversions at the call site.)
Approach 1:
I can use an extern template declaration to tell other TUs that foo<int>() is instantiated elsewhere:
// In foo.hpp
template <typename T, typename = std::enable_if_t<std::is_same_v<T, int>>>
void foo(T i);
extern template void foo(int);
// In foo.cpp
template <typename T, typename>
void foo(T i) { ... } // full template definition
template void foo(int); // explicit instantiation with T = int
Approach 2:
I can provide an explicit specialisation for the int case:
// In foo.hpp
template <typename T, typename = std::enable_if_t<std::is_same_v<T, int>>>
void foo(T i);
template <> void foo(int i); // explicit specialisation declaration (*)
// In foo.cpp
template <>
void foo(int i) { ... } // explicit specialisation definition
Questions:
Are both of these approaches legal, or am I unintentionally relying on UB?
If both are legal, is there a good reason to prefer one approach over the other, other than the fact that approach 2 is very slightly less typing? Both GCC and Clang work equally well with either approach.
For approach 2, is the explicit specialisation declaration at (*) actually required? Again, both GCC and Clang are perfectly happy if it's omitted, but doing so makes me uncomfortable about the fact that a call to foo(3) in another TU is an implicit instantiation of a template with no definition visible, and no promise that such a definition exists elsewhere.
There is a third approach. In approach 3 you specify the function you want to have and the you add a template overload and mark that as delete. That looks like
void foo(int i)
{
// stuff
}
template <typename T>
void foo(T t) = delete;
Since the template version will match all types exactly it will be preferred in all cases except int since a non template exact match is preferred to a template one. So you will only be able to call foo with an int and all other types will give you an error that they are trying to call the deleted function void foo(T t).
Live Example
The following bit of code fails to compile on gcc 4.5.3
struct Frobnigator
{
template<typename T>
void foo();
template<typename T>
void bar();
};
template<typename T>
void Frobnigator::bar()
{
}
template<typename T>
void Frobnigator::foo()
{
bar<T>();
}
template<> // error
void Frobnigator::foo<bool>()
{
bar<bool>();
}
template<>
void Frobnigator::bar<bool>()
{
}
int main()
{
}
Error message: specialization of ‘void Frobnigator::bar() [with T = bool]’ after instantiation. I finally resolved this problem by having the specialization of Frobnigator::bar<bool>() appear before Frobnigator::foo<bool>(). Clearly the order in which the methods appear matter.
Why then is the following lite version of the above code, in which the the specialization of bar appears after the generic version, valid ?
struct Frobnigator
{
template<typename T>
void foo();
};
template<typename T>
void Frobnigator::bar()
{
}
template<>
void Frobnigator::bar<bool>()
{
}
int main()
{
}
Your first code is not correct by standard.
n3376 14.7.3/6
If a template, a member template or a member of a class template is explicitly specialized then that specialization shall be declared before the first 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.
In your case - implicit instantiation of bar function with type bool is required by its usage in foo<bool>, before explicit specialization declaration.
Clearly the order in which the methods appear matter.
Indeed; as is usually the case in C++, you can't use something before it's declared, and this applies to explicit template specialisations as well as most other things.
Using bar<bool> (by calling it from foo<bool>) without a previous declaration of an explicit specialisation causes that specialisation to be instantiated from the generic template, if it hasn't already been. You'll need at least a declaration of the explicit specialisation to prevent that.
Why is this the case, considering that the specialization of bar appears after the generic version in the following lite version of the above code
The second example differs by not instantiating foo<bool> at all. The issue isn't that the specialisation is declared after the generic template (which must be the case), but that it's declared after that specialisation has already been instantiated.
What has the meaning the "using of template keyword in the function declaration"?
In this example compiler errors with error: "func" is not a template function.
template<typename T>
struct Window {
T value;
};
template void func(Window<int>, int);
template<typename T>
void func(Window<T>, T) {
}
int main(void) {
}
But below example is ok.
template<typename T>
struct Window {
T value;
};
template<typename T>
void func(Window<T>, T) {
}
template void func(Window<int>, int);
int main(void) {
}
What is the meaning with "template" in the above case?
Is it just indicator that this function is template function?
A declaration that begins with the keyword template and does not have the <pointy braces> immediately afterward is called an explicit instantiation. It means to look up the template definition and plug in the specified template parameters to get a specific function specialization or class specialization. So in the first example, the compiler is complaining it can't instantiate func(Window<int>, int) because it doesn't yet know anything about a template called func.
Explicit instantiations are usually not necessary, since templates can be implicitly instantiated just by attempting to use them. But explicit instantiation gives some control over where and how related linker symbols appear, and can in some cases be used to move a template definition out of a header file into a source file.
This is just because in your first example you have the definition of the template
template<typename T>
void func(Window<T>, T) {
}
after the forced instantiation that's represented by this line:
template void func(Window<int>, int);
Whereas in the second example they're the right way around.
But the comment to your question is fair, you are asking a basic question ('what does template mean'), which is best answered by a thoroughgoing study of the subject.