Function templates: extern template vs explicit specialisation - c++

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

Related

Empty angle-brackets after a function's name in explicit specialization

Here is a code from the article Why not specialize function templates?
template<class T>
void f(T); // (1)
template<class T>
void f(T*); // (2)
template<>
void f<>(int*); // (3)
My question is about the last declaration. What does that syntax mean? When we want to fully specialize a function template, e.g. (1), for some type we usually write:
template<>
void f<int>(int);
i.e. we put that type into the angle-brackets after the function' name.
So what does the syntax (3) means?
In your case,
template<> void f<>(int*);
is an explicit specialization of
template<class T> void f(T*);
base template. It's the same as
template<> void f<int>(int*);
just the template argument is deduced.
You can even write:
template<> void f(int*);
with the same effect. A similar case is presented on cppreference, see section Explicit specializations of function templates, where there is written:
When specializing a function template, its template arguments can be omitted if template argument deduction can provide them from the function arguments.
The relevant part of the C++ Standard: http://eel.is/c++draft/temp.expl.spec#11.
A missing template argument will be deduced by the compiler if you don't provide it. In this particular case it will be deduced to int* because the second template is more specialized (but both are the candidates). So
template<> void f<>(int*)
will become
template<> void f<int>(int*)
after type deduction.

C++ explicit specialization of member template belonging to template class

In the current C++ standard draft, there is this example in this paragraph belonging to the section related to explicit specialization of templates:
template<class T> struct A {
void f(T);
template<class X1> void g1(T, X1);
template<class X2> void g2(T, X2);
void h(T) { }
};
// specialization
template<> void A<int>::f(int);
// out of class member template definition
template<class T> template<class X1> void A<T>::g1(T, X1) { }
// member template specialization
template<> template<class X1> void A<int>::g1(int, X1); //(1)
// member template specialization
template<> template<>
void A<int>::g1(int, char); //(2)
In (1) it seems more like g1 was specialized to be still a function template in the specialized version of A (A< int >), while in (2) it seems like g1 is itself specialized for its own set template parameters ( (int, from A< int >), char).
I find there to be a difference between these specializations (again, (1) feels like declaring a new version of g1 to be used for a "special version" of its "container" A, while (2) feels like a specialization regarding g1 itself (or regarding its own template parameters).
Furthermore, consider this example:
template<class T> struct A{
int f() { return 1; }
}
template<>
int A<int>::f() { return 2; } //(3)
To me (1) and (3) are the same "kind of specialization", one that is linked to a special version of the "container", while (2) is a specialization of the entity (template) itself.
Does the standard mention this difference or are these two kinds of specialization referred to as the same?
Thank you.
First, your #3 is equivalent to the first specialization in the cited example, except that the standard’s f uses the class’s template parameter in its signature—presumably to illustrate that, since the signature must match for a specialization, template arguments for the class may need to be repeated in the specialization declaration.
Then, the difference is that #1 is a specialization of a member of A<T> when T is int—a sort of shorthand for writing a specialization of A<int> itself that is mostly the same as the primary template. In particular, to avoid being misleading, the signature for the member being specialized must be unchanged from the (instantiation of the) primary template. In this case, that means it’s still a template.
On the other hand, #2 is a specialization of a template that happens to be a member of A<int>—the specialization is of A<int>::g1 itself, not of “A<T>::g1 when T is int” as for #1. This of course applies only when the member is a template, unlike #3. (In this case, #2 is a specialization of the very template declared by #1!)
Part of the point of [temp.expl.spec]/15 is that these two cases are not strongly distinguished syntactically. The difference is largely academic: both are surgical changes to a templated entity for certain arguments.

The meaning of template keyword in the function declaration

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.

How are template definitions matched to template declarations?

How exactly is a template declaration matched to a template definition? I found some text in the standard about template-ids referring to the same function if "their template-names [...] refer to the same template and [...]" (14.4 [temp.type] p1) but I can't find a definition for template-names or when template-names refer to the same template. I'm not sure if I'm on the right track anyway because I haven't deciphered the grammar well enough to tell if a template-id is part of the definition/declaration of a template, or just a use of a template.
For example, the following program works fine.
#include <iostream>
template<typename T>
T foo(T t);
int main() {
foo(1);
}
template<typename T>
T foo(T t)
{ std::cout << "A\n"; return 0; }
If I change the way I use the template parameters in the template definition the names apparently no longer refer to the same template, and linking fails.
#include <iostream>
template<typename T>
T foo(T t);
int main() {
foo(1);
}
template<typename T>
int foo(T t) { std::cout << "A\n"; return 0; }
// or
template<typename T>
struct identity {
typedef T type;
};
template<typename T>
typename identity<T>::type
foo(T t) { std::cout << "A\n"; return 0; }
Next, if I move the template definition to another translation unit, for my implementation of C++ (MSVC 11 beta) the program works no matter how I say the types.
//main.cpp
template<typename T>
T foo(T t);
int main() {
foo(1);
}
//definition.cpp
#include <iostream>
template<typename T>
struct identity {
typedef T type;
};
template<typename T>
typename identity<T>::type
foo(T t) { std::cout << "A\n"; return 0; }
template int foo<int>(int);
or
//definition.cpp
#include <iostream>
template<typename T>
int foo(T t) { std::cout << "A\n"; return 0; }
template int foo<int>(int);
or even if the definition isn't a template at all:
//definition.cpp
#include <iostream>
int foo(T t) { std::cout << "A\n"; return 0; }
Obviously linking is succeeding because the signature/mangled name is the same regardless of the template that was instantiated to create the symbol. I think this undefined behavior because I'm violating:
§ 14.1 [temp] p6
A function template, member function of a class template, or static
data member of a class template shall be defined in every translation
unit in which it is implicitly instantiated (14.7.1) unless the
corresponding specialization is explicitly instantiated (14.7.2) in
some translation unit; no diagnostic is required.
But then say I try to fulfill those requirements by putting a definition of the template in the second translation unit, and including an explicit instantiation at one of two locations:
#include <iostream>
template<typename T>
T foo(T t) { std::cout << "A\n"; return 0; }
// Location 1
template<typename T>
int foo(int t) { std::cout << "B\n"; return 0; }
// Location 2
What are the rules about disambiguating what template an explicit instantiation refers to? Putting it at Location 1 causes the correct template to be instantiated and that definition to be used in the final program, while putting it at Location 2 instantiates the other template, and causes what I believe is undefined behavior under 14.1 p6 above.
On the other hand an implicit instantiation of two templates definitions picks the first template no matter what, so it seems like the rule for disambiguating the templates is different in these circumstances:
#include <iostream>
template<typename T>
T foo(T t) { std::cout << "A\n"; return 0; }
template<typename T>
int foo(int t) { std::cout << "B\n"; return 0; }
int main() {
foo(1); // prints "A"
}
The reason this came up is related to this question where the questioner discovered that a single forward declaration
template<typename T>
T CastScriptVarConst(const ScriptVar_t& s);
could not act as a declaration of multiple template definitions:
template<typename T>
typename std::enable_if<GetType<T>::value < SVT_BASEOBJ,T>::type
CastScriptVarConst(const ScriptVar_t& s) {
return (T) s;
}
template<typename T>
typename std::enable_if<!(GetType<T>::value < SVT_BASEOBJ)
&& std::is_base_of<CustomVar,T>::value,T>::type
CastScriptVarConst(const ScriptVar_t& s) {
return *s.as<T>();
}
And I wanted to better understand the relationship between template definitions and declarations.
OK, let's start from the beginning. The "template-name" of a template is the actual name of the function or class being templated; that is, in
template<class T> T foo(T t);
foo is the template name. For function templates, the rule for deciding whether they are the same is quite long, described in 14.5.5.1 "Function template overloading". Paragraph 6 of that section (I'm quoting from C++03 here, so the wording and paragraph numbers might have changed in C++11) defines the terms equivalent and functionally equivalent, when applied to expressions involving template parameters.
In short, equivalent expressions are the same apart from possibly having different names for template parameters, and functionally equivalent expressions are the same if they happen to evaluate to the same thing. For example, the first two f declarations are equivalent, but the third is only functionally equivalent to the other two:-
template<int A, int B>
void f(array<A + B>);
template<int T1, int T2>
void f(array<T1 + T2>);
template<int A, int B>
void f(array< mpl::plus< mpl::int<A>, mpl::int<B> >::value >);
It goes on in paragraph 7 to extend those two definitions to whole function templates. Two function templates that match (in name, scope, and template parameter lists) are equivalent if they also have equivalent return types and argument types, or functionally equivalent if they only have functionally equivalent return types and argument types. Looking at your second example, these two functions are only functionally equivalent:-
template<typename T>
T foo(T t);
template<typename T>
typename identity<T>::type foo(T t);
Paragraph 7 closes with the dire warning that, "If a program contains declarations of function templates that are functionally equivalent but not equivalent, the program is ill-formed; no diagnostic is required." Your second example is, thus, not valid C++. Detecting errors like that would require each declaration and definition of a function template to be annotated in the binary with an AST describing the template expression each parameter and the return type came from, which is why the standard doesn't require implementations to detect it. MSVC is justified in compiling your third example how you intended, but it would be just as justified to break.
Moving on to explicit instantiation, the important section is 14.7, "Template instantiation and specialization". Paragraph 5 disallows all of the following:
Explicitly instantiating a template more than once;
Explicitly instantiating and explicitly specializing the same template;
Explicitly specializing a template for the same set of arguments more than once.
Again, "no diagnostic is required" as it's quite hard to detect.
So to expand your explicit instantiation example, the following code breaks the second rule and is illegal :-
/* Template definition. */
template<typename T>
T foo(T t)
{ ... }
/* Specialization, OK in itself. */
template< >
int foo(int t)
{ ... }
/* Explicit instantiation, OK in itself. */
template< >
int foo(int t);
This is illegal regardless of the locations of the explicit specialization and the explicit instantiation, but of course because no diagnostic is required, you may get useful results on some compilers. Note also the difference between explicit instantiation and explicit specialization. The following example is ill-formed because it declares an explicit specialization without defining it:-
template<typename T>
T f(T f)
{ ... }
template< >
int f(int);
void g(void)
{ f(3); }
but this example is well-formed, because it has an explicit instantiation:-
template<typename T>
T f(T f)
{ ... }
template f(int);
void g(void)
{ f(3); }
The < > makes all the difference. Be warned also that even when you do define an explicit specialization, it has to be before you use it, otherwise the compiler might already have generated an implicit instantiation for that template. This is in 14.7.3 "Explicit specialization" paragraph 6, just below where you were reading, and again, no diagnostic is required. To adapt the same example, this is ill-formed:-
template<typename T>
T f(T f)
{ ... }
void g(void)
{ f(3); } // Implicitly specializes int f(int)
template< >
int f(int) // Too late for an explicit specialization
{ ... }
If you weren't confused enough yet, take a look at your last example:-
template<typename T>
T foo(T t) { ... }
template<typename T>
int foo(int t) { ... }
The second definition of foo is not a specialization of the first definition. It would have to be template< > int foo(int) to be a specialization of template<typename T> T foo(T). But that's OK: function overloading is allowed, and it's allowed between function templates and normal functions. Calls of the form foo(3) will always use the first definition, because its template parameter T can be deduced from the argument type. The second definition does not allow its template parameter to be deduced from the argument type. Only by explicitly specifying T can you reach the second definition, and only then when the call is not ambiguous with the first definition:-
f<int>(3); // ambiguous
f<string>(3); // can only be the second one
The whole process of doing overload resolution for function templates is too long to describe here. Read section 14.8.3 if you're interested and ask more questions :-)

Getting confused with C++ template

I am looking at some c++ code and do not understand the purpose of the template declaration in this situation:
template<> void operator>>(const ClassA& s, const ClassB d) {...}
What is the semantic of template<>?
This is, indeed, template specialization, as others before mentioned. There must be some previously declared function template, such as:
template<typename T, typename U>
void operator>>(const T& s, const U d) {...}
However, it is quite misguided. It is much better to remove the template<> altogether, so operator>> would just be overloaded. The problem with function template specialization is that it may lead to unexpected behaviour in the presence of overloaded functions (and operator>> has lots of overloads), since the specialization does not overload. This means that the compiler first selects the most appropriate overload for the function and then, if the selected overload is a function template, it looks for template specializations to see if there is an appropriate one.
A classical example (unfortunately, I don't remember where I read it). Consider this overloaded function template:
template <typename T>
void Function(T param);
template <typename T>
void Function(T* param);
template <>
void Function(int* param);
main()
{
int i = 5;
Function(&i);
}
As expected, the template specialization for int* is called. But just change the order of the function definitions:
template <typename T>
void Function(T param);
template <>
void Function(int* param);
template <typename T>
void Function(T* param);
main()
{
int i = 5;
Function(&i);
}
Now the general template for T* is called, since we are specializing the template for T, not for T*, and this second template is better suited for our call. This would be avoided if we overloaded the function instead of specializing the template:
void Function(int* param);
Now the order of declaration does not matter, we will always call the overload for int*.
UPDATE: Now I know who to credit. I read about this in an article by Herb Sutter. The example was provided by Peter Dimov and Dave Abrahams.
This is Template specialization
You use this syntax when you want to provide a special handler for a particular template type. Consider:
// A normal template definition.
template <typename AType>
whoami () { std::cout << "I am an unknown type."; }
// Now we specialize.
template <>
whoami<int> () { std::cout << "I am an integer!"; }
There's some other nonsense involved, particularly "partial specialization", but that's the basic function of template <>.
It is a template specialization: (fully or partially) resolving the template for a specific type. (Your particular example seems to be a full specialization, as no more template parameters are left unresolved in it. If the template has multiple parameters, and you specialize only some of them, it is called partial template specialization)
This allows one to provide type-specific optimizations for a given template, or to do many clever tricks such as detecting the static type of variables etc.