To simplify the discussion, this article only summarizes the name bindings of the function template, and then discusses the related content of the class template. Name Binding
Name binding are the names that appear in the template definition (also known as constructing ' construct ', including operators, etc.), by querying in the relevant context and binding to the declaration process, for example,
int all = 0;
Template<typename t>
int sum (t* T, int s)
{for
(int index = 0; index < s; index++) all
+ = T[index ];
return all;
}
For example, all that appears here is a name, and the compiler resolves to that name by making 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 name that appears in the template (also includes the operator and so on), the corresponding is divided into two kinds, one is called dependent name, the other is called Non-dependent name. Dependent name and non-dependent name
Dependent name is its specific type that depends on the template parameters, is not determined when the template is defined, and needs to be deferred until the template is instantiated to determine the name, such as:
Example 1
int i = 0;
Template<typename t>
void g (T t)
{
typename t::value V;
F (t);
i++;
}
Here both V and F are dependent on the type parameter T, so all are dependent name, and I is non-dependent name. For Non-dependent name, you should be able to uniquely determine (BIND) in the template definition context , which is consistent with the rules in common functions. For Non-dependent name, it should be noted that once you bind to a specific association object, it cannot be changed, even when the template is instantiated later, there is a more appropriate match, and can no longer bind to the new object.
Example 2
//main.cpp
#include <iostream>
void f (double) {std::cout << "f (Double)" << std:: Endl }
template<typename t>
void g (T t)
{
F (1); Non-dependent name, which is bound when the template is parsed
;
void f (int) {std::cout << "f (int)" << Std::endl;}
int main ()
{
g (1);
}
We take the mainstream compiler as an example, give the results of different compilers
compiler |
Compilation 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) |
As a result, it seems that gcc and clang more in line with the standard definition of the Non-dependent name resolution range, that is, a match can be found in the context of the template definition and bound to the match, which can no longer be changed. VC + + seems at this point did not follow the standard to perform the lookup, but the search for Non-dependent name was postponed. To further prove our judgment, make a slight change to the code to remove the definition of the F (double) function before the template function g:
Example 3
//main.cpp
#include <iostream>
template<typename t>
void g (T t)
{
F (1); Non-dependent name, which is bound when the template is parsed
;
void f (int) {std::cout << "f (int)" << Std::endl;}
int main ()
{
g (1);
}
compiler |
Compilation Parameters |
Results |
GCC 4.9.3 |
g++-wall-std=c++11-o2-o a.out main.cpp |
Error:there are no arguments to ' f ' and depend on a template parameter, so a declaration of ' F ' must is 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 ' |
At the moment it is clear that VC + + does not follow the standard definition, to find non-dependent Name. We found a description of VC + +, nonstandard 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 is 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 above instructions are for the latest Visual Studio version, VS2015.
GCC for this non-standard behavior, there is a compilation option,-fpermissive, you can do not conform to the standard behavior, allow the compiler to pass. To demote a standard:
Sample |
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 the standard is so defined.
It may be felt that Visual C + + is more reasonable for a deferred query for Non-dependent name, because the more likely it is that the compiler will find a more matching object, but why the standard does not allow this seemingly more excellent behavior. The answer is not to allow the template's specificity to appear inconsistent definition. That is, for a particular template-specific (consistent template parameter), the definition must be consistent throughout the program (ODR), and consider the following scenario:
Example 4
//main.cpp
#include <iostream>
void f (double) {std::cout << "f (Double)" << std:: Endl }
template<typename t>
void g (T t)
{
F (1);
};
void Func1 ()
{
g (1); The first call to the template function, parameter type ' int '
}
void f (int) {std::cout << "f (int)" << Std::endl;}
void Func2 ()
{
g (1); The second call to the template function, the parameter type ' int '
}
int main ()
{
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 results, two calls, it seems that all compilers succeeded in generating consistent template specificity. However, as Visual C + + allows you to defer the scope of the query to the template instantiation Context , there is a significant potential risk. For a compilation unit, the compiler can ensure that only one copy is produced, but if multiple compilation units use the same type of argument, the same type of template specificity is generated , but these compilation units have a different invocation context, it is possible that the resulting special versions are inconsistent because Non-dependent name may be matched to various objects that the behavior compiler may not be aware of and lead to unpredictable consequences, and in view of this, The standard requires non-dependent name to bind immediately when defining a template appears to be an effective means of preventing this ' morbid ' behavior.
The second reason, of course, is to standardize the implementation of different compilers so that they behave the same way for the same code, but it appears that Microsoft's VC + + seems to have overlooked this point.
For Dependent-name, because the template definition cannot confirm their specific identity, it can only be deferred until the template is instantiated . Point of instantiation
Template instantiation is the process of generating a specific instance of a template, which is called a special template for a function template, and as a user displays a specialized instance, similar to a normal function, because the template parameter is already determined. It is to be noted that the special version of the template generated by the compiler is sometimes called "generated generated specialization" or "implicit implicit specialization". To distinguish between custom explicit specificity (explicit specialization) in a program or user-defined specificity (user-defined specialization).
What is generated for the specificity of the template function. And where was it generated. The vast majority of C + + articles in this section of the explanation is vague, and even contradictory, which also caused a different compiler interpretation, so sometimes a piece of code in a compiler can run smoothly, and under another compiler but not compile.
Some specific code examples are given below:
Example 5
//main.cpp
#include <iostream>
using namespace std;
void f (const char*)
{
cout << "f (const char*) \ n";
}
Template <class t>
void g (T a)
{
F (a);
}
int main ()
{
g ("Hello");//Call F (const char*) return
0;
With regard to this Code, F is clearly a dependent-name, so its binding needs to be deferred until the template is instantiated. As explained above, we know that for Non-dependent name, the compiler will search for the name 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 time void F ( The const char*) is before the template definition, so this function will soon be bound.
So what happens if we swap the positions of F and g? In theory, F is a dependent-name, so the compiler searches when the template is instantiated, and we call the template function g, which occurs after F is defined, so the binding behavior should not change.
Example 6
//main.cpp
#include <iostream>
using namespace std;
Template <class t>
void g (T a) //swap f and g position
{
F (a);
}
void f (const char*) //definition F
{
cout << "f (const char*) \ n";
}
int main ()
{
g ("Hello"); F has already been defined return
0;
}
The following are the results of the run on the 3 compiler:
compiler |
error or correct run-time output information |
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 to the translation unit void F (const char*) |
|
|
Visual C + + 19.00 |
F (const char*) |
|
|
Clang 3.7.0 |
Call to function ' F ' is neither visible in the template definition nor 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 Visual C + + successfully resolves to the function f defined after the template, and GCC and clang the error messages that are basically the same:
SN |
| Message
1 |
Not visible in the template definition (template definitions context not visible) |
2 |
Not found by Argument-dependent lookup (ADL) |
The first error message should now be fairly clear, the context of the template definition clearly does not include function f, because it is defined after the template function, and the second error message is that ADL does not find any functions that can be matched, because the template argument type is ' const char* ', not a user-defined type , so no ADL lookup was performed, and the corresponding function was not found.
Based on the above information, we push back, if the ADL can be executed correctly, then we can 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 ADL. From this, it seems to be confirmed that the Name binding process 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 is also found through ADL. However, for GCC and clang, the object is not found directly in the context of the template instantiation, while VC + + expands the search scope 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 that it is not allowed to look up non-dependent name in the template instantiation context, just to avoid violating the ODR, 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 template instantiation context to avoid ambiguity, why do you allow the ADL lookup in the context of the template instantiation? This is not the same will allow ambiguity.
Example 8
//ff.h
#include <iostream>
namespace N
{
class X {};
int g (X, int i);
}
Template<typename t>
double ff (T T, double D)
{return
g (T, d);
}
Ff.cpp
#include "ff.h"
int n::g (X, int i) {std::cout << "G (x,int)" << Std::endl;
Double x1 = FF (n::x{}, 1.1);
Main.cpp
#include "ff.h"
namespace N
{
Double g (X, double D) {std::cout << "G (x,double)" & lt;< Std::endl; return D; }
Auto x2 = FF (n::x{}, 2.2);
int main ()
{
extern double x1;
std::cout<< "x1 =" << x1 << Std::endl;
Std::cout << "x2 =" << x2 << Std::endl;
return 0;
}
Although this code, eventually all compilers are successfully bound to int g (X, int i), but it seems that this is the credit of the compiler, rather than the standard mandatory requirements of the rules. The compiler has the potential to generate two inconsistent template<n::x> double ff (n::x, double), a problem that is really confusing. This, of course, also suggests that reducing the dependency of the template code on the context is a good programming specification. 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> ();
}
Here operator+ is defined in the global scope, and type A is really anonymous namespace, so the compiler is not able to find the operator+ in the global scope, but GCC is still compiled. Puzzle 3
The following code can be compiled with VC + +:
Example
//main.cpp
#include <iostream>
using namespace std;
Template <typename t> void foo () {
g ());
}
namespace {
class A {};
}
int main ()
{
foo<a> ();
}
void G (a)
{
cout << "G (a)" << Endl;
}
This should not be called ' confusing ', VC + + will delay the binding to the final link stage, or when the link to the stage to generate template specificity, at this time all the compilation units are visible. Therefore, after the function g is defined to the main function, it is still valid. When it comes to template-specific code where, when to generate, there is no uniform rule, it may be the first time the template is called, it is possible at the end of each compilation unit, but also may be the link stage, but also may be a combination of several methods.