简体   繁体   English

什么时候模板类的特殊成员函数被实例化?

[英]When are special member functions of a template class instantiated?

When are the special member functions (specifically, copy/move constructors and copy/move assignment operators) of a template class instantiated? 实例化模板类的特殊成员函数(特别是复制/移动构造函数和复制/移动赋值运算符)是什么时候? As soon as the class itself is instantiated, or only when they are needed? 一旦类本身被实例化,或者仅在需要它们时?

This comes up in the following situation: 这出现在以下情况:

template <class T, class U>
struct pair
{
    T first;                 
    U second;                

    pair() : first(), second() {}

    pair(const pair&) = default;
};

struct S
{
    S() {}
    S(const S&) = delete;
    S(S&&) = default;
};

int main()
{
    pair<int, S> p;
}

Clang refuses to compile this code, with the following errors: Clang拒绝编译此代码,但有以下错误:

test.cpp:9:5: error: the parameter for this explicitly-defaulted copy constructor is const, but a member or base requires it to be
      non-const
    pair(const pair&) = default;
    ^
test.cpp:21:18: note: in instantiation of template class 'pair<int, S>' requested here
    pair<int, S> p;
                 ^

suggesting that it tries to instantiate the copy constructor as soon as the class is instantiated. 建议它在实例化类时立即尝试实例化复制构造函数。

GCC, however, compiles the code just fine, suggesting that it would only try to instantiate the copy constructor if it was actually needed. 但是,GCC编译代码就好了,这表明它只会在实际需要时尝试实例化复制构造函数。

Which compiler's behaviour is correct? 哪个编译器的行为是正确的?

(A similar discrepancy is exhibited for assignment operators.) (赋值算子也有类似的差异。)

UPDATE : This has something to do with the fact that the copy constructor of pair in this example is default ed, because if I change its definition to 更新 :这与此示例中pair的复制构造函数是default编辑的事实有关,因为如果我将其定义更改为

pair(const pair& p) : first(p.first), second(p.second) {}

then the code passes clang as well. 然后代码也传递了clang。

Have a look at section 14.7.1 of the current C++11 standard. 看看当前C ++ 11标准的第14.7.1节。 To quote from the n3242 version of the draft: 引用草案的n3242版本:

The implicit instantiation of a class template specialization causes the implicit instantiation of the declarations, but not of the definitions or default arguments, of the class member functions, member classes, static data members and member templates; 类模板特化的隐式实例化会导致类成员函数,成员类,静态数据成员和成员模板的声明的隐式实例化,而不是定义或默认参数的隐式实例化。 and it causes the implicit instantiation of the definitions of member anonymous unions. 它会导致成员匿名联合的定义的隐式实例化。 Unless a member of a class template or a member template has been explicitly instantiated or explicitly specialized, the specialization of the member is implicitly instantiated when the specialization is referenced in a context that requires the member definition to exist; 除非已显式实例化或明确专门化类模板或成员模板的成员,否则在需要成员定义存在的上下文中引用特化时,将隐式实例化成员的特化; in particular, the initialization (and any associated side-effects) of a static data member does not occur unless the static data member is itself used in a way that requires the definition of the static data member to exist. 特别是,除非静态数据成员本身以需要静态数据成员的定义存在的方式使用,否则不会发生静态数据成员的初始化(以及任何相关的副作用)。

So, this means, when you use a class as a type as above only the declarations are instantiated with it. 因此,这意味着,当您使用类作为上面的类型时,只有声明用它实例化。 So the actual (defaulted) implementation of the copy constructor should not be instantiated, as it is not needed in the above code. 因此,不应实例化复制构造函数的实际(默认)实现,因为在上面的代码中不需要它。 So GCC is handling this correctly, whereas Clang does not. 所以GCC正确处理这个问题,而Clang却没有。

Also your edit suggests, that Clang is generating the implementation for the default copy constructor too early, since your directly implemented copy constructor is faulty as well (you cannot call the copy constructor for S as you are doing in your own implementation). 另外你的编辑建议,Clang过早地为默认的复制构造函数生成实现,因为你的直接实现的复制构造函数也是错误的(你不能像在自己的实现中那样调用S的复制构造函数)。 Since the defaulted implementation and your implementation should be the same in all respects (including the time of instantiation), I would consider this a clang bug. 由于默认的实现和你的实现在所有方面都应该是相同的(包括实例化的时间),我会认为这是一个铿锵的bug。

The relevant passage of the standard is [dcl.fct.def.default]/1: 标准的相关段落是[dcl.fct.def.default] / 1:

A function that is explicitly defaulted shall [...] have the same declared function type (except for possibly differing ref-qualifiers and except that in the case of a copy constructor or copy assignment operator, the parameter type may be "reference to non-const T ", where T is the name of the member function's class) as if it had been implicitly declared 显式默认的函数应具有相同的声明函数类型(除了可能不同的ref限定符之外,在复制构造函数或复制赋值运算符的情况下,参数类型可以是“引用非-const T “,其中T是成员函数类的名称),就像它已被隐式声明一样

This rule applies even if the defaulted function is never used. 即使从未使用默认功能,此规则也适用。 Now, [class.copy]/9 says: 现在,[class.copy] / 9说:

The implicitly-declared copy constructor will have the form 隐式声明的复制构造函数将具有该表单

X::X(const X&)

if [...] for all the non-static data members of X that are of a class type M [...], each such class type has a copy constructor whose first parameter is of type const M& or const volatile M& . 如果[]对于类型为M [...]的X所有非静态数据成员,则每个这样的类类型都有一个复制构造函数,其第一个参数的类型为const M&const volatile M&

Otherwise the implicitly-declared copy constructor will have the form 否则,隐式声明的复制构造函数将具有该表单

X::X(X&)

Therefore, an example like this is ill-formed (and should produce the diagnostics you are seeing): 因此,像这样的例子是不正确的(并且应该产生你看到的诊断):

struct A {
  A();
  A(A&); // Note, non-const type A in copy constructor
};
template<typename T>
struct B {
  T t;
  B();
  B(const B&) = default;
};
B<A> b; // error, B<A>::B(const B&) is defaulted but has the wrong type

However, in your example, this rule doesn't apply. 但是,在您的示例中,此规则不适用。 Due to a clang bug (which is already fixed), deleted copy constructors were incorrectly considered to have non-const parameter types, leading to this error. 由于clang bug(已经修复),删除的拷贝构造函数被错误地认为具有非const参数类型,从而导致此错误。

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

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