标准为何不将模板构造函数视为副本构造函数?

时间:2019-04-25 09:22:06

标签: c++ templates constructor language-lawyer copy-constructor

这是复制构造函数[class.copy.ctor/1]的定义:

  

如果类X的第一个参数的类型为X&,const X&,volatile X&或const volatile X&类型,并且没有其他参数,或者所有其他参数都具有默认参数( [dcl.fct.default])。

为什么标准不将模板作为复制构造函数?

在这个简单的示例中,两个构造函数都是副本构造函数:

struct Foo {
    Foo(const Foo &); // copy constructor
    Foo(Foo &);       // copy constructor
};

请参阅以下类似示例:

struct Foo {
     Foo() = default;

     template <typename T>
     Foo(T &) {
         printf("here\n");
     }
};

int main() {
    Foo a;
    Foo b = a;
}

在此示例中,将打印here。因此,看来我的模板构造函数是一个复制构造函数,至少它的行为类似于一个(在通常调用复制构造函数的上下文中被调用)。

为什么文本中存在“非模板”要求?

3 个答案:

答案 0 :(得分:31)

让我们将模板放置一秒钟。如果一个类未声明副本构造函数,则将生成一个隐式默认的副本构造函数。可以将其定义为已删除,但仍默认为默认值。

成员模板不是成员函数。仅在需要时才从中实例化成员。

那么,编译器如何仅从类定义中知道是否需要使用T = Foo进行专门化?不可以但这正是需要决定如何处理对隐式默认副本构造函数(AND移动构造函数)的潜在需求的基础。变得混乱了。

最简单的方法是排除模板。无论如何,我们总会有一些拷贝构造函数,它默认会做正确的事情 TM ,并且由于它不是从模板中实例化的,因此将受到重载解析的青睐。

答案 1 :(得分:10)

  

为什么文本中存在“非模板”要求?

鉴于情况有所不同,复制构造函数可以是模板。在存在复制构造函数模板的情况下,非复制构造函数如何不歧义?考虑一下:

struct Foo {
   // ctor template: clearly useful and necessary
   template <typename T>
      Foo(const T&) {}

   // copy ctor: same signature! can't work
   template <typename T>
      Foo(const T &) {}
};

此外,可以通过转换或普通构造来从非Foo的对象构造Foo,但可以从非Foo的对象进行拷贝构造将复制的概念更改为包括转换的复制。但这可以通过现有方案(转换或非复制构造)来实现。

  

在此示例中,将在此处打印。所以看来我的模板构造函数是复制构造函数

您显示的示例不会调用副本构造,而是一个普通的隐式构造。如果您将构造函数模板更改为

template <typename T>
Foo(const T &) {
//  ^^^^^
    printf("here\n");
}

然后Foo b = a;导致编译器生成的副本构造函数被调用。请注意,由编译器生成的副本ctor具有以下签名:

Foo(const Foo&);

这需要向const中的a添加一个Foo b = a;限定词。您的代码段中的原始构造函数模板Foo(T&)是更好的匹配,因为未添加const限定符。

答案 2 :(得分:0)

复制构造函数的格式为X(X&)或(X const&),如果您自己没有声明,则编译器将为您提供该副本构造函数。如果使用模板类,则可能由于问题而出现非模板。

比方说,有一个模板类具有模板副本构造函数。问题在于,当您使用具有相同模板类型的该类的另一个实例实例化该类时,将不会调用您的模板副本构造函数。

问题不是您的副本构造器模板不匹配。问题在于隐式副本构造函数不是函数模板,而在涉及重载解析时,非模板优先于模板专业化。

来源: C++ template copy constructor on template class