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

时间:2012-04-29 08:16:57

标签: c++ templates c++11 clang copy-constructor

模板类的特殊成员函数(具体来说,复制/移动构造函数和复制/移动赋值运算符)何时实例化?一旦类本身被实例化,或者仅在需要它们时?

出现以下情况:

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拒绝编译此代码,但有以下错误:

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;
                 ^

建议在实例化类后立即尝试实例化复制构造函数。

然而,GCC编译代码很好,表明它只会在实际需要时尝试实例化复制构造函数。

哪种编译器的行为是正确的?

(赋值算子表现出类似的差异。)

更新:这与此示例中pair的复制构造函数为default的事实有关,因为如果我将其定义更改为

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

然后代码也传递了clang。

2 个答案:

答案 0 :(得分:5)

查看当前C ++ 11标准的第14.7.1节。引用草案的n3242版本:

  

类模板特化的隐式实例化导致   声明的隐式实例化,但不是   类成员函数的定义或默认参数,   成员类,静态数据成员和成员模板;而且它   导致成员定义的隐式实例化   匿名工会。除非是类模板或成员的成员   模板已被显式实例化或明确专门化,   当成员的专业化被隐式地实例化时   在需要成员的上下文中引用特化   定义存在;特别是初始化(和任何   除非发生静态数据成员的相关副作用   静态数据成员本身以需要的方式使用   要存在的静态数据成员的定义。

因此,这意味着,当您使用类作为上面的类型时,仅使用它实例化声明。因此,不应实例化复制构造函数的实际(默认)实现,因为在上面的代码中不需要它。所以GCC正确处理这个问题,而Clang却没有。

此外,您的编辑建议,Clang过早地为默认复制构造函数生成实现,因为您的直接实现的复制构造函数也是错误的(您不能像调用S那样调用复制构造函数你自己的实现)。由于默认的实现和您的实现在各方面都应该是相同的(包括实例化的时间),我认为这是一个铿锵的错误。

答案 1 :(得分:2)

标准的相关段落是[dcl.fct.def.default] / 1:

  

明确默认的功能   [...]   具有相同的声明函数类型(可能不同的ref限定符除外,在复制构造函数或复制赋值运算符的情况下,参数类型可以是“引用非const T”,其中{{ 1}}是成员函数类的名称),好像它已被隐式声明

即使从未使用过默认功能,此规则也适用。现在,[class.copy] / 9说:

  

隐式声明的复制构造函数将具有

形式      

T

     

如果[{1}}的所有非静态数据成员都是类类型X::X(const X&) [...],则每个这样的类类型都有一个复制构造函数,参数的类型为XM

     

否则隐式声明的复制构造函数将具有

形式      

const M&

因此,这样的例子是不正确的(并且应该产生你所看到的诊断):

const volatile M&

但是,在您的示例中,此规则不适用。由于clang bug(已经修复),删除的拷贝构造函数被错误地认为具有非const参数类型,导致此错误。