In order to simplify the discussion, this article only summarizes the name binding of the function template, and then discusses the related content of the class template.
Name Binding
Name bindings are names that appear in the template definition (also called construct ' construct ', also include operators, etc.), are queried in the context of the correlation, and bound to the declaration process, for example,
int0;template<typename T>intsumint s){ for(intindex0indexindex++) all += t[index]; return all;}
Here, for example, is the one where the all name compiler resolves to the name to make a decision about which object to bind to and when to bind.
The Name binding is divided into two steps, the first step is when the template is defined (point of Definition), and the second step is when the template is instantiated (point of instantiation). And the template appears in the name (also includes operators, etc.), the corresponding is divided into two, one is called dependent name, the other is called Non-dependent name.
Dependent Name and non-dependent name
Dependent name is its specific type depends on the template parameters, when the template definition can not be determined, you need to defer to the template instantiation When the name can be determined, such as:
//示例1int0;template<typename T>void g(T t){ typename T::Value V; f(t); i++;}
Here V and f all depend on the type parameter T , therefore all is dependent name, and i then is non-dependent name. For Non-dependent name, you should define the context in the template , it is possible to uniquely determine (BIND), which is consistent with the rules in the normal function. For Non-dependent name, it is important to note that once bound to a specific associated object, it can not be changed, even when the template is instantiated later, there is a more appropriate match, will not be able to bind to the new object.
///Example 2//main.cpp#include <iostream>voidFDouble) {STD::cout<<"F (double)"<<STD:: Endl; }Template<TypeNameT>voidG (T T) {f (1);//non-dependent name, which is bound when the template is parsed};voidFint) {STD::cout<<"F (int)"<<STD:: Endl; }intMain () {g (1);}
We take the mainstream compiler as an example, give the results of different compilers run
| compiler |
Compile Parameters |
Run Results |
| GCC 4.9.3 |
g++ -Wall -std=c++11 -O2 -o a.out main.cpp |
F (Double) |
| Visual C + + 19.00 |
cl.exe main.cpp -o a.exe /MD |
f (int) |
| Clang 3.7.0 |
clang++ -Wall -std=c++11 -O2 -o a.out main.cpp |
F (Double) |
From the results, it seems that gcc and clang are more compliant with the definition of the Non-dependent name resolution range, that is, a match can be found in the context of the template definition, bound to the match, and cannot be changed later. VC + + seems to be at this point does not follow the criteria to perform the lookup, but the Non-dependent name lookup is postponed. To further prove our judgment, make a slight change to the code, removing the g definition of the function before the template function f(double) :
//example 3 Main.cpp #include <iostream> Template <typename t>void g (t t) { F (1 ); //non-dependent name, binding } when parsing the template; void F (int ) {std :: cout << "f (int)" << std :: Endl; }int Main () {g (1 );}
| compiler |
Compile Parameters |
Results |
| GCC 4.9.3 |
g++ -Wall -std=c++11 -O2 -o a.out main.cpp |
Error:there is no arguments to ' f ' this depend on a template parameter, so a declaration of ' F ' must be available [-fper Missive] |
| Visual C + + 19.00 |
cl.exe main.cpp -o a.exe /MD |
f (int) |
| Clang 3.7.0 |
clang++ -Wall -std=c++11 -O2 -o a.out main.cpp |
Error:use of undeclared identifier ' F ' |
It is clear at the moment that the VC++ non-dependent Name is not found in the standard definition. We found the VC++ description of non-standard behavior (non-standard Behavior) on the following MSDN page:
The Visual C + + compiler does not currently support binding
Non-dependent names when initially parsing a template. This does not
Comply with section 14.6.3 of the C + + ISO specification. This can
Cause overloads declared after the template (but before the template
is instantiated) to be seen.
Note: The instructions above are for the current version of Visual Studio VS2015 .
GCC for this nonstandard behavior, there is a compile option,-fpermissive, can be used for some non-conforming behavior, allowing the compilation to pass. That is, the standard is degraded processing:
| Example |
compilation Options |
Results |
| 2 |
g++ -Wall -std=c++11 -fpermissive -O2 -o a.out main.cpp |
F (Double) |
| 3 |
g++ -Wall -std=c++11 -fpermissive -O2 -o a.out main.cpp |
f (int) |
Why is the standard so defined?
Some might find it Visual C++ more reasonable to defer queries for the non-dependent name, because the more likely the compiler is to find more matching objects, but why does the standard not allow this seemingly superior behavior? The answer is that there is no inconsistent definition of the template's specificity. That is, for a particular template specification (consistent template parameters), it must be defined consistently (ODR)throughout the program, considering the following scenario:
///Example 4//main.cpp#include <iostream>voidFDouble) {STD::cout<<"F (double)"<<STD:: Endl; }Template<TypeNameT>voidG (T T) {f (1);};voidFunc1 () {g (1);//First call to template function, parameter type ' int '}voidFint) {STD::cout<<"F (int)"<<STD:: Endl; }voidFunc2 () {g (1);//Second call to template function, parameter type ' int '}intMain () {func1 (); Func2 ();}
| compiler |
compilation Options |
Results |
| GCC 4.9.3 |
g++ -Wall -std=c++11 -O2 -o a.out main.cpp |
F (Double) f (double) |
| Visual C + + 19.00 |
cl.exe main.cpp -o a.exe /MD |
f (int) f (int) |
| Clang 3.7.0 |
clang++ -Wall -std=c++11 -O2 -o a.out main.cpp |
F (Double) f (double) |
From the result, two calls, it seems, all compilers have succeeded in generating consistent template specificity. However, Visual C++ there is a great potential risk of allowing the query scope to be deferred to the template instantiation context , but for a compilation unit, the compiler can guarantee that only one copy is produced, but if multiple compilation units use the same type of argument to generate the same type of template specificity, However, these compilation units have different invocation contexts, so it is possible that these generated versions are inconsistent because the Non-dependent name may match up to various objects, which the behavior compiler may not be aware of and cause unintended consequences, The standard requires non-dependent name to be binding immediately when defining a template appears to be an effective preventative means of reducing this ' morbid ' behavior.
Of course, the second reason is to standardize different compiler implementations, so that they have the same behavior for the same code, but it seems that Microsoft's VC + + appears to ignore this point.
For Dependent-name, because the template definition can not confirm their specific identity, can only be deferred until the template instantiation of the time to consider.
Point of instantiation
Template instantiation is the process of generating a specific instance of a template, which is called template specificity for the instance generated by the function template, similar to an instance of a user's display of a particular function, since the template parameters are now determined. It is important to note that the special version of the template generated by the compiler is sometimes called "generated generated specialization" or "implicit implicit specialization". Explicit specialization or user-defined specificity (user-defined specialization), as distinct from the custom explicit customization in the program.
For the specificity of template functions, what is generated? And where was it generated? The vast majority of C + + articles in this part of the explanation is very vague, and even contradictory, which also caused a different compiler interpretation of the difference, so sometimes a piece of code in a certain compiler can run smoothly, and under another compiler cannot compile.
Some specific code examples are given below:
//示例5//main.cpp#include <iostream>usingnamespacestd;void f(constchar*){ cout"f(const char*)\n";}template <classvoid g(T a){ f(a);}int main(){ g("hello"//调用f(const char*) return0
The code f is obviously a dependent-name, so its binding needs to be deferred until the template is instantiated. With the above explanation, we know that for Non-dependent name, the compiler will search for names in the context of the template definition, but for dependent-name, the compiler will still follow this step, first searching for the object in the template definition context, at which point void f(const char*) Before the template definition, so this function will soon be bound.
So what happens if we switch f g positions? Theoretically, f it is a dependent-name, so the compiler searches when the template is instantiated, and we call the template function g after F is defined, so there should be no change in the binding behavior.
//example 6 //main.cpp Span class= "Hljs-preprocessor" > #include <iostream> using namespace STD ; template <class t> void g (T a) //replace F and g position {f (a);} void F (const char *) //definition f {cout << " f (const char*) \ n ";} int Main () {g ( "hello" ); //f has been defined by the return 0 ;}
Here are the results of the run on the 3 compiler:
| compiler |
error or output information when running correctly |
| GCC 4.9.3 |
Error: ' F ' is not declared in this scope, and no declarations were found by Argument-dependent lookup at the point of INS Tantiation [-fpermissive] f (a); Note: ' Void f (const char*) ' declared here, later in the translation Unit void F (const char*) |
|
|
| Visual C + + 19.00 |
F (const char*) |
|
|
| Clang 3.7.0 |
Call to function ' F ' was neither visible in the template definition or found by Argument-dependent lookup F (a); Note:in instantiation of function template specialization ' G ' requested here G ("Hello"); Note: ' F ' should is declared prior to the call site void F (const char*) |
Obviously only the visual C++ functions that have been successfully parsed into the template are defined, f gcc and clang the error message given is basically the same:
| SN |
Message |
| 1 |
Not visible in the template definition (templates define context not visible) |
| 2 |
Not found by Argument-dependent lookup (ADL) |
The first error message should now be quite clear, the context of the template definition obviously does not contain the function f , because it is defined after the template function, the second error message is that ADL does not find any function that can match, because the template argument type is " const char* ', not a user-defined type, So there is no search for the ADL at all, and the corresponding function is not found naturally.
According to the above information, we push back, if the ADL can be executed correctly, is it possible to find the function? f
//example 7 Main.cpp #include <iostream> Using namespace std ; template <class t> void g (T a) {f (a);} namespace mynamespace{class x{}; void f (X) {cout << " f (X) \ n "; }}int Main () {g (mynamespace::x{}); return 0 ;}
| compiler |
Output |
| GCC 4.9.3 |
F (X) |
| Visual C + + 19.00 |
F (X) |
| Clang 3.7.0 |
F (X) |
At this point, the three compilers correctly found the correct function through the ADL. Thus, it seems to be confirmed that the process of Name binding will always first search for objects in the context of the template definition, whether for dependent-name or non-dependent Name, for Dependent-name, The second step of the search is also done through ADL. However gcc , for and clang , the object is not looked up directly in the context of the template instantiation, and the VC++ search scope is extended to the template instantiation context.
Puzzle 1
We can understand that GCC and clang do not allow direct lookup of dependent name directly in the template instantiation context, and are not allowed to find non-dependent name in the template instantiation context, just to avoid violating the ODR, To avoid creating inconsistent template instances in different compilation units. But why does the standard allow ADL lookups in the context of template instantiation? The answer is I don't know, because if you're not allowed to look in the context of a template instantiation in order to avoid ambiguity, why do you allow ADL lookups in the context of template instantiation? Wouldn't that be the same as allowing ambiguity?
///Example 8//ff.h#include <iostream>namespacen{classX {};intG (X,inti);}Template<TypeNameT>DoubleFF (T T,DoubleD) {returnG (T, d);}//ff.cpp#include "ff.h"intN::g (X,inti) {STD::cout<<"G (x,int)"<<STD:: Endl;returnI }Doublex1 = FF (n::x{},1.1);//main.cpp#include "ff.h"namespacen{DoubleG (X,DoubleD) {STD::cout<<"G (x,double)"<<STD:: Endl;returnD }}Autox2 = FF (n::x{},2.2);intMain () {extern DoubleX1;STD::cout<<"x1 ="<< X1 <<STD:: Endl;STD::cout<<"x2 ="<< X2 <<STD:: Endl;return 0;}
Although this code, eventually all the compilers are bound to successfully int g(X, int i) , but it seems that this is the compiler's credit, rather than the standard mandatory rules. The compiler is likely to generate two inconsistencies template<N::X> double ff(N::X, double) , and the problem is really confusing. This, of course, also shows that reducing the dependency of the template code on the context is a good programming rule.
Puzzle 2
Although it is wrong to search for a function declaration directly in the context of a template instantiation, the GCC compiler does not consider the following code to be a problem.
//example 9 Main.cpp #include <iostream> Using namespace std ; template <typename t> void foo () {t () + t ();} namespace {class A {};} void operator + (const a& const a&) {cout << "operator+ (const n::a& Const n::a&) " << Endl;} int Main () {foo<a> ();}
operator+the definition here is in the global scope, and the type is A namespace, so the compiler has no way to find the global scope operator+ , but it gcc is still compiled.
Puzzle 3
The following code vc++ can be compiled by:
//example #include <iostream> Using namespace std ; template <typename t> void foo () {g (T ());} namespace {class A {};} int Main () {foo<a> ();} void g (a a) {cout << " G (A) " << Endl;}
This should not be called ' confusion ', vc++ delaying the binding to the last link stage, or creating template specificity when it comes to the link phase, when all the compilation units are visible. Therefore, after the function is g defined to the main function, it is still valid. When it comes to where the template-specific code is, when it is generated, there is no uniform rule, it is possible that at the end of the first invocation of the template, it is possible that at the last of each compilation unit, there may be a link phase or a mixture of several methods.
C + + function template Name binding