简体   繁体   English

在哪个位置发生模板实例化绑定?

[英]At which point occurs template Instantiation binding?

This code is from "C++ programming language" by Bjarne Stroustrup (C.13.8.3 Point of Instantiation Binding) 此代码来自Bjarne Stroustrup的“C ++编程语言”(C.13.8.3实例化绑定点)

template <class T>
void f(T value)
{
    g(value);
}

void g(int v);

void h()
{
    extern g(double);
    f(2);
}

And he mentions: 他提到:

Here, the point of instantiation for f() is just before h(), so the g() called in f() is the global g(int) rather than the local g(double). 这里,f()的实例化点恰好在h()之前,因此在f()中调用的g()是全局g(int)而不是局部g(double)。 The definition of ''instantiation point'' implies that a template parameter can never be bound to a local name or a class member. “实例化点”的定义意味着模板参数永远不能绑定到本地名称或类成员。

void h()
{
    struct X {}; // local structure
    std::vector<X> v; // error: can't use local structure as template parameter
}

My questions are: 我的问题是:

  1. Why should the first code work? 为什么第一个代码有效? g() is declared later, and I really get an error with G++ 4.9.2 that g isn't declared at that point. g()稍后声明,我真的得到G ++ 4.9.2的错误,那时g没有被声明。

  2. extern g(double) - how this works? extern g(double) - 这是如何工作的? since return value doesn't matter in case of function overloading, then we can miss it in forward declarations? 因为在函数重载的情况下返回值无关紧要,那么我们可以在前向声明中错过它吗?

  3. the point of instantiation for f() is just before h() - why? f()的实例化点就在h()之前 - 为什么? isn't it logical that it'll get instantiated when f(2) is being called? 在调用f(2)时它会被实例化是不合逻辑的? Right where we call it, whence g(double) will be in scope already. 就在我们称之为的地方, g(double)已经在范围内了。

  4. The definition of ''instantiation point'' implies that a template parameter can never be bound to a local name or a class member - Has this changed in C++14? “实例化点”的定义意味着模板参数永远不能绑定到本地名称或类成员 - 在C ++ 14中是否已更改? I'm getting error with C++(G++ 4.9.2), but don't get error with C++14(G++ 4.9.2). 我在使用C ++(G ++ 4.9.2)时遇到错误,但是在C ++ 14(G ++ 4.9.2)中没有出错。

"In 1985, the first edition of The C++ Programming Language was released, which became the definitive reference for the language, as there was not yet an official standard ." “1985年,第一版的C ++编程语言发布,成为该语言的权威参考,因为还没有官方标准 。” wiki C++ History So it didn't change between C++11 and C++14. wiki C ++历史因此它在C ++ 11和C ++ 14之间没有变化。 I can assume (and please take this with a grain of salt) it changed between "pre-standardization" and standardization. 我可以假设(并且请带着一点点盐)它在“预标准化”和标准化之间发生了变化。 Maybe someone who knows better the history of C++ can shed more light here. 也许更了解C ++历史的人可以在这里获得更多的亮点。

As for what actually happens: 至于实际发生的事情:


First let's get out of the way the simple one: 首先让我们摆脱简单的方式:

extern g(double);

This is invalid C++. 这是无效的C ++。 Historically, unfortunately C allowed omission of type. 从历史上看,不幸的是C允许遗漏类型。 In C++ you have to write extern void g(double) . 在C ++中,你必须编写extern void g(double)


Next, let's ignore the g(double) overload to answer your first question: 接下来,让我们忽略g(double)重载来回答你的第一个问题:

template <class T>
void f(T value)
{
    g(value);
}

void g(int v);

int main()
{
    f(2);
}

In C++ there is the infamous two phase name lookup: 在C ++中有臭名昭着的两阶段名称查找:

  • In the first phase, at the template definition, all non-dependent names are resolved. 在第一阶段,在模板定义中,解析所有非依赖名称 Failure to do so is a hard error; 不这样做是一个很难的错误;
  • Dependent names are resolved in phase two, at the template instantiation. 在模板实例化时,在第二阶段解析从属名称。

The rules are a bit more complicated, but that is the gist of it. 规则有点复杂,但这就是它的要点。

g is dependent on template parameter T so it passes the first phase. g取决于模板参数T因此它通过第一阶段。 That means that if you never instantiate f , the code compiles just fine. 这意味着如果你从不实例化f ,代码编译就好了。 At the second phase f is instantiated with T = int . 在第二阶段, fT = int实例化。 g(int) is now searched, but not found: 现在搜索g(int) ,但未找到:

 17 : error: call to function 'g' that is neither visible in the template definition nor found by argument-dependent lookup g(value); ^ 24 : note: in instantiation of function template specialization 'f<int>' requested here f(2); ^ 20 : note: 'g' should be declared prior to the call site void g(int v); 

In order for an arbitrary name g to pass with flying colors we have a few options: 为了使任意名称g能够通过飞行颜色,我们有几个选择:

  1. Declare g previously: 先前声明g
void g(int);

template <class T>
void f(T value)
{
    g(value);
}
  1. bring g in with T : 带来gT
template <class T>
void f(T)
{
    T::g();
}

struct X {
   static void g();
};

int main()
{
    X x;
    f(x);
}
  1. Bring g in with T via ADL: 带来gT经由ADL:
template <class T>
void f(T value)
{
    g(value);
}

struct X {};

void g(X);

int main()
{
    X x;
    f(x);
}

These of course change the semantics of the program. 这些当然会改变程序的语义。 They are meant to illustrate what you can and cannot have in a template. 它们旨在说明您在模板中可以和不可以拥有的内容。


As for why doesn't ADL find g(int) , but finds g(X) : 至于为什么ADL找不到g(int) ,但找到g(X)

§ 3.4.2 Argument-dependent name lookup [basic.lookup.argdep] §3.4.2依赖于参数的名称查找[basic.lookup.argdep]

  1. For each argument type T in the function call, there is a set of zero or more associated namespaces and a set of zero or more associated classes to be considered [...]: 对于函数调用中的每个参数类型T,有一组零个或多个关联的命名空间以及一组零个或多个关联的类要考虑[...]:

    • If T is a fundamental type, its associated sets of namespaces and classes are both empty. 如果T是基本类型,则其关联的命名空间和类集都是空的。

    • If T is a class type (including unions), its associated classes are: the class itself; 如果T是类类型(包括联合),则其关联的类是:类本身; the class of which it is a member, if any; 它所属的成员,如果有的话; and its direct and indirect base classes. 及其直接和间接基类。 Its associated namespaces are the namespaces of which its associated classes are members. 其关联的名称空间是其关联类是成员的名称空间。 [...] [...]


And finally we get to why extern void g(double); 最后我们得出为什么extern void g(double); inside main is not found: first of all we showed that g(fundamental_type) is found iff it is declared prior to the f definition. 找不到内部主要内容:首先,我们发现如果f定义之前声明了g(fundamental_type)则会找到它。 So let's make it void g(X) inside main . 所以让我们在main里面使它成为void g(X) Does ADL find it? ADL找到了吗?

template <class T>
void f(T value)
{
    g(value);
}

struct X{};


int main()
{
  X x;
  void g(X);

  f(x);
}

No. Because it does not reside in the same namespace as X (ie global namespace) ADL can't find it. 不会。因为它与X (即全局命名空间)不在同一名称空间中,所以ADL找不到它。

Proof that g is not in global 证明g不在全球范围内

int main()
{
  void g(X);

  X x;
  g(x); // OK
  ::g(x); // ERROR
}

34 : error: no member named 'g' in the global namespace; 34:错误:全局命名空间中没有名为'g'的成员; did you mean simply 'g'? 你的意思是'g'吗?

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM