C++ 转发构造函数调用基类的复制构造函数2次
下面的代码将构造函数从基类转发到派生类 为什么要调用2个复制构造函数?背景是什么 用g++编译C++ 转发构造函数调用基类的复制构造函数2次,c++,c++11,constructor,C++,C++11,Constructor,下面的代码将构造函数从基类转发到派生类 为什么要调用2个复制构造函数?背景是什么 用g++编译 #include <iostream> using namespace std; struct A { A() { cout << "A" << endl; } A(const A&) { cout << "A(const A&)" << endl; } template<typename T&
#include <iostream>
using namespace std;
struct A {
A() { cout << "A" << endl; }
A(const A&) { cout << "A(const A&)" << endl; }
template<typename T> A(T a); // Needed to compile :-O
};
template<typename T>
struct C : public T { using T::T; };
int main()
{
A a;
C<A> ca(a);
//C<A> caa(ca);
return 0;
}
对A::A(const A&)
的一个调用是C
的基类构造函数
调用另一个来复制pass by value参数。通过在
a
中定义构造函数模板,C
将获得具有类似签名的构造函数模板。其隐含定义类似于:
template<typename T>
struct C : public T
{
//using T::T;
C() = default;
C(C const&) = default;
template<typename U> C(U a) : T( std::forward<U>(a) ) {}
};
模板
结构C:公共T
{
//使用T::T;
C()=默认值;
C(C const&)=默认值;
模板C(ua):T(std::forward(a)){
};
现在调用A
的复制构造函数两次:一次用于按值获取参数。第二次调用的结果是调用a的复制构造函数T(std::forward(a))
。这让我感到惊讶,因为您希望继承的构造函数调用它所继承的基类的确切构造函数。但事实并非如此,重载解析选择的不是A
的ctor模板,而是纯拷贝ctorA(常数&)
(见下文)
有趣的是,它并不关心A
中的构造函数模板做什么,只需要声明它。这就是为什么在OP中,定义可能会丢失;它也可以被删除(这可能是一个缺陷?)
只有在初始化T(std::forward(A))
的重载解析期间,才必须选择A
的复制选择器。这里的情况是这样的:参数是一个类型为A
的右值,它可以直接绑定到常量A&
引用,这是A的复制构造函数所要求的。由于引用绑定是直接绑定的,并且不进行从基到基的转换,因此复制构造函数的排名是完全匹配的。A
中的ctor模板也被列为精确匹配,但由于重载集中有一个具有相同级别的模板和非模板函数,因此首选非模板函数(复制ctorA(常数&)
)。请发布实际代码。你发布的内容没有编译:@JimBuck好的,我添加了一个模板构造函数,它会编译。。。出于同样的原因,根据12.9/8:“表达式列表中的每个表达式的形式为static_cast(p)
,其中p
是相应构造函数参数的名称,T
是p
的声明类型。”我认为应该是T(static_cast(a))
,因此,它应该更喜欢A
的移动构造函数而不是复制构造函数(如果有)。@Casey是的,但这正是forward
所做的,我认为forward
可能更常见/更容易理解。如何防止双重调用?@KarolyHorvath,这取决于上下文。通常,您可以编写具有完美转发的构造函数,因此可以使用模板a(T&&)代码>在A
中,但这太贪婪了,需要受SFINAE的限制。另一种方法是在C
中定义额外的系数。如果您为该示例提供一些上下文,也许我们可以找到一个合适的解决方案。@DyP对A(const&)
的解释比A&&
参数的模板构造函数更好。
template<typename T>
struct C : public T
{
//using T::T;
C() = default;
C(C const&) = default;
template<typename U> C(U a) : T( std::forward<U>(a) ) {}
};