How are template definitions matched to template declarations? - c++

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 :-)

Related

Function templates: extern template vs explicit specialisation

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

C++: Why does the compiler instantiate a template when it is not used

Let's consider this code:
template <bool c>
class A {
public:
A() = default;
// I want to enable f(int) only if c == true
template<typename Temp = typename enable_if<c>::type>
void f(int val) {
cout << val << endl;
};
};
int main() {
A<false> a;
A<true> b;
b.f(543);
}
When I try to compile this I get the following error:
error: no type named 'type' in 'struct std::enable_if<false, void>'
But I don't use the template method f(int) when the argument <bool c> is false then it shouldn't exist.
The compiler does not "instantiate" your template, as you seem to incorrectly believe. The compiler is simply trying to parse and analyze your template declaration, which is a part of your class definition. If the class is instantiated, then all member declarations have to be valid. Your member template declaration is not valid. Hence the error.
If some template is "not used", it means that it does not get specialized and instantiated. But the declaration of that template still has to be valid. And the validity of those parts of that declaration that do not depend on template parameters is checked immediately. In other words, what you wrote in your code is no different from
template <typename T = jksgdcaufgdug> void foo() {}
int main() {}
or, closer to your situation
template <typename T = std::enable_if<false>::type> void foo() {}
int main() {}
Even though these programs do not "use" (do not instantiate) function template foo, it still does not mean that the declaration of foo can contain random garbage like jksgdcaufgdug or explicitly refer to non-existent entities like std::enable_if<false>::type. The above examples will not compile for that reason.
You can use "random garbage" in dependent contexts, like
template <typename T> void foo(typename T::kjhdfjskhf x)
{
typename T::jksgdcaufgdug i;
}
and you can use std::enable_if in dependent contexts like
template <typename T,
typename U = typename enable_if<is_void<T>::value>::type>
void bar()
{
}
and it will not produce "early" errors, but in your case enable_if<c> does not depend on Temp, so it is not in dependent context. Which means that the correctness of typename enable_if<c>::type is checked immediately when you instantiate A<false>.

decltype(auto) in member function ignores invalid body, decltype(expr) fails

I have a simple templated wrapper struct with a member function calling .error() on an object of its template type.
template <typename T>
struct Wrapper {
T t;
decltype(auto) f() {
return t.error(); // calls .error()
}
};
If I instantiate this with a type that doesn't have an error() member function, it's fine as long as I don't call it. This is the behavior I want.
Wrapper<int> w; // no problem here
// w.error(); // uncommented causes compilation failure
If I use what I thought was the semantic equivalent with a trailing return type, it errors on the variable declaration
template <typename T>
struct Wrapper {
T t;
auto f() -> decltype(t.error()) {
return t.error();
}
};
Wrapper<int> w; // error here
I'll accept that the two are not semantically equivalent, but is there anyway to get the behavior of the former using a trailing return type (C++11 only) without specializing the whole class with some kind of HasError tmp trickery?
The difference between the versions
decltype(auto) f();
auto f() -> decltype(t.error());
is that the function declaration of the second can be invalid. Return type deduction for function templates happens when the definition is instantiated [dcl.spec.auto]/12. Although I could not find anything about return type deduction for member functions of class templates, I think they behave similarly.
Implicitly instantiating the class template Wrapper leads to the instantiation of the declarations, but not of the definitions of all (non-virtual) member functions [temp.inst]/1. The declaration decltype(auto) f(); has a non-deduced placeholder but is valid. On the other hand, auto f() -> decltype(t.error()); has an invalid return type for some instantiations.
A simple solution in C++11 is to postpone the determination of the return type, e.g. by turning f into a function template:
template<typename U = T>
auto f() -> decltype( std::declval<U&>().error() );
The definition of that function worries me a bit, though:
template<typename U = T>
auto f() -> decltype( std::declval<U&>().error() )
{
return t.error();
}
For the specializations of Wrapper where t.error() is not valid, the above f is a function template that cannot produce valid specializations. This could fall under [temp.res]/8, which says that such templates are ill-formed, No Diagnostic Required:
If no valid specialization can be generated for a template, and that template is not instantiated, the template is ill-formed, no diagnostic
required.
However, I suspect that rule has been introduced to allow, but not require, implementations to check for errors in non-instantiated templates. In this case, there is no programming error in the source code; the error would occur in instantiations of the class template described by the source code. Therefore, I think it should be fine.
An alternative solution is to use a fall-back return type to make the function declaration well-formed even if the definition is not (for all instantiations):
#include <type_traits>
template<typename T> struct type_is { using type = T; };
template <typename T>
struct Wrapper {
T t;
template<typename U=T, typename=void>
struct error_return_type_or_void : type_is<void> {};
template<typename U>
struct error_return_type_or_void
<U, decltype(std::declval<U&>().error(), void())>
: type_is<decltype(std::declval<U&>().error())> {};
auto f() -> typename error_return_type_or_void<>::type {
return t.error();
}
};
One approach is a tagged crtp.
// todo:
template<class T>
struct has_error; // true_type if T.error() is valid
template<class D,class T,bool Test=has_error<T>{}>
struct do_whatever {
D* self(){return static_cast<D*>(this);}
D const* self()const{return static_cast<D const*>(this);}
auto f()->decltype(self()->t.error()) {
return self()->t.error();
}
};
template<class D,class T>
struct do_whatever<D,T,false>{};
now f() just isn't there if T has no error().

Why SFINAE doesn't work in right side in default function arguments?

I have this code:
struct My
{
typedef int foo;
};
struct My2
{
};
template <typename T>
void Bar(const T&, int z = typename T::foo())
{
std::cout << "My" << std::endl;
}
void Bar(...)
{
std::cout << "..." << std::endl;
}
int main()
{
My my;
Bar(my); // OK
My2 my2;
Bar(my2); // Compile error: no type named ‘foo’ in ‘struct My2’
return 0;
}
I suppose, that if some class T doesn't have typedef foo inside, compiler should exclude first overload and choose overload with ellipsis. But I check this code on MSVC, gcc and clang and I get compile error on those compilers. Why SFINAE doesn't work in this case?
The type of z is not subject to template substitution, it is always int. This means there is no opportunity for SFINAE, and you instead get a compiler error when attempting to resolve T::foo for the default value. Default arguments do not participate in overload resolution, instead being instantiated only when missing from the function call. Section 14.7.1 (paragraphs 13/14) of the standard describes this behaviour, but does not give justification for the lack of SFINAE here.
SFINAE can be allowed to happen by making the type of z a template parameter, as below:
(live example: http://ideone.com/JynMye)
#include <iostream>
struct My
{
typedef int foo;
};
struct My2
{
};
template<typename T, typename I=typename T::foo> void Bar(const T&, I z = I())
{
std::cout << "My\n";
}
void Bar(...)
{
std::cout << "...\n";
}
int main()
{
My my;
Bar(my); // OK
My2 my2;
Bar(my2); // Also OK
return 0;
}
This will use the "My" version for the first call, and the "..." version for the second call. The output is
My
...
However, if void Bar(...) was a template, for whatever reason, the "My" version will never get a chance:
(live example: http://ideone.com/xBQiIh)
#include <iostream>
struct My
{
typedef int foo;
};
struct My2
{
};
template<typename T, typename I=typename T::foo> void Bar(const T&, I z = I())
{
std::cout << "My\n";
}
template<typename T> void Bar(T&)
{
std::cout << "...\n";
}
int main()
{
My my;
Bar(my); // OK
My2 my2;
Bar(my2); // Also OK
return 0;
}
Here, the "..." version is called in both cases. The output is:
...
...
One solution is to use class template (partial) specialisation; provide the "..." version as the base, with the type of the second parameter defaulted to int, and the "My" version as a specialisation where the second parameter is typename T::foo. In conjunction with a plain template function to deduce T and dispatch to the appropriate class' member function, this produces the desired effect:
(live example: http://ideone.com/FanLPc)
#include <iostream>
struct My
{
typedef int foo;
};
struct My2
{
};
template<typename T, typename I=int> struct call_traits {
static void Bar(...)
{
std::cout << "...\n";
}
};
template<typename T> struct call_traits<T, typename T::foo> {
static void Bar(const T&, int z=typename T::foo())
{
std::cout << "My\n";
}
};
template<typename T> void Bar(const T& t)
{
call_traits<T>::Bar(t);
}
int main()
{
My my;
Bar(my); // OK
My2 my2;
Bar(my2); // Still OK
return 0;
}
Here, the output is:
My
...
The type z is an int, is not being deduced by the compiler, no room for SFINAE to take place. The value being used to initialise z is based on the default of T::foo, which doesn't exist; hence the error.
If the type for z is elevated to the template itself, substitution can now fail, and SFINAE kicks in.
#include <iostream>
struct My
{
typedef int foo;
};
struct My2
{
};
template <typename T, typename I = typename T::foo>
void Bar(const T&, I z = I())
{
(void)z; // silence any warnings on unused
std::cout << "My" << std::endl;
}
void Bar(...)
{
std::cout << "..." << std::endl;
}
int main()
{
My my;
Bar(my);
My2 my2;
Bar(my2); // Compiles
return 0;
}
Live sample
In order for a function template to be part of the overloaded list of candidate functions, the template argument deduction must succeed. If it fails, then the candidate is removed from the list. Hence, if no deduction failure occurs, it is added to the candidate list (but this does not preclude further errors if it is finally selected).
14.8.3/1 Overload resolution
A function template can be overloaded either by (non-template) functions of its name or by (other) function templates of the same name. When a call to that name is written (explicitly, or implicitly using the operator notation), template argument deduction (14.8.2) and checking of any explicit template arguments (14.3) are performed for each function template to find the template argument values (if any) that can be used with that function template to instantiate a function template specialization that can be invoked with the call arguments. For each function template, if the argument deduction and checking succeeds, the template arguments (deduced and/or explicit) are used to synthesize the declaration of a single function template specialization which is added to the candidate functions set to be used in overload resolution. If, for a given function template, argument deduction fails, no such function is added to the set of candidate functions for that template. The complete set of candidate functions includes all the synthesized declarations and all of the non-template overloaded functions of the same name. The synthesized declarations are treated like any other functions in the remainder of overload resolution, except as explicitly noted in 13.3.3.
Template argument deduction is performed on the function type and its template arguments themselves.
14.8.2/8 Template argument deduction
If a substitution results in an invalid type or expression, type deduction fails. An invalid type or expression is one that would be ill-formed, with a diagnostic required, if written using the substituted arguments. [ Note: If no diagnostic is required, the program is still ill-formed. Access checking is done as part of the substitution process. —end note ] Only invalid types and expressions in the immediate context of the function type and its template parameter types can result in a deduction failure.
From the OP, the function Bar<T> is added to the candidate list since it can be deduced what the type for T is. It is instantiated and the default arguments are checked, and hence it fails.
14.7.1/13 Implicit instantiation
If a function template f is called in a way that requires a default argument to be used, the dependent names are looked up, the semantics constraints are checked, and the instantiation of any template used in the default argument is done as if the default argument had been an initializer used in a function template specialization with the same scope, the same template parameters and the same access as that of the function template f used at that point. This analysis is called default argument instantiation. The instantiated default argument is then used as the argument of f.
Quotes taken from draft n3797
One more C++03 compatible option for you. Because in answers above default argument was used in template function and it is not permitted in standard.
#include <iostream>
struct TypeWithFoo{
typedef int Foo;
};
template<typename T, bool>
struct onFooAction;
template<typename T>
struct onFooAction<T, false>{
void operator ()(const T &t){
std::cout << "No foo :(\n";
}
};
template<typename T>
struct onFooAction<T, true>{
void operator ()(const T &t){
std::cout << "Foo =)\n";
}
};
template<typename T>
struct hasFoo{
typedef char yes[1];
typedef char no[2];
template<typename C>
static yes& testForFoo(typename C::Foo*);
template<typename>
static no& testForFoo(...);
static const bool value = sizeof(testForFoo<T>(0)) == sizeof(yes);
};
template<typename T>
void bar(const T &t){
onFooAction<T, hasFoo<T>::value>()(t);
}
int main(){
bar(10);
bar(TypeWithFoo());
}

Is this explicit template function specialization of a member template of a member template of a class template valid?

Does anyone know if this explicit specialization is or is not valid:
template <class>
struct L {
template <typename T>
struct O {
template <typename U>
static void Fun(U);
};
};
template<>
template<typename T>
template<typename U>
void L<int>::O<T>::Fun(U) {}
clang trunk (12/3/2013) gives the following error:
f:...\test.cpp:36:20: error: out-of-line definition of 'Fun' from class 'O' without definition
void L<int>::O<T>::Fun(U) {}
~~~~~~~~~~~~~~^
1 error generated.
Any supporting references from the standard to justify your answer will be greatly appreciated!
Note: I am somewhat surprised that it is an error - I would expect the specialization to be picked for any family of template arguments with which 'Fun' is instantiated beginning with <int><?any><?any>.
Is this a clang bug or a bug in my expectations?
Thank you!
====== EDIT (I think I have an answer) ========
OK - i think i found the supporting wording - from N3797 (post-chicago 2013 working draft) - 14.7.3/16 =>
"In an explicit specialization declaration for a member of a class template or a member template that appears in namespace scope, the member template and some of its enclosing class templates may remain unspecialized, except that the declaration shall not explicitly specialize a class member template if its enclosing class templates are not explicitly specialized as well."
If i interpret this correctly, we need an explicit specialization of O if we're going to declare an explicit specialization of its member? Hence the error.
Correct?
Thanks!
I don't believe it is valid. I'm not deep enough in the language to tell you how/if you can do this without providing the top-level specialization as done below, or if there is a shortcut for skipping the replicate templates, but the error message is fairly clear: You're trying to provide an implementation of a static member of a dependent nested type without providing the specialization of the actual dependency. I.e. this works:
#include <iostream>
template <typename>
struct L
{
template <typename T>
struct O
{
template <typename U>
static void Fun(U)
{
std::cout << "General: " << __PRETTY_FUNCTION__ << std::endl;
};
};
};
// provide specialized L
template<>
struct L<int>
{
template <typename T>
struct O
{
template <typename U>
static void Fun(U);
};
};
// L<int> is a specialized type, so provide the rest.
template<typename T>
template<typename U>
void L<int>::O<T>::Fun(U)
{
std::cout << "Special: " << __PRETTY_FUNCTION__ << std::endl;
}
int main()
{
L<int>::O<double> nobj;
nobj.Fun(10);
L<double>::O<int> nobj2;
nobj2.Fun(20);
return 0;
}
Output
Special: static void L<int>::O<double>::Fun(U) [T = double, U = int]
General: static void L<double>::O<int>::Fun(U) [T = int, U = int]