是 VC2015 的 BUG 还是 C++ 的什么特性?

www_adintr_com 2016-06-21 02:05:44
加精
1.

#include <cstdio>
#include <type_traits>
#include <typeinfo>
#include <vector>

class AA
{
public:
template <typename ImpInterface>
AA(ImpInterface&& impobj)
{
printf("construct use type %s\n", typeid(impobj).name());
}

private:
};



void test_language()
{
std::vector<AA> a{ AA(10) };
}


Debug 版, 关闭优化的情况下, 输出:
construct use type int
后正常结束, 没看到 拷贝构造 的调用.

2.

#include <cstdio>
#include <type_traits>
#include <typeinfo>
#include <vector>

class AA
{
public:
AA(const AA& a) {};

template <typename ImpInterface>
AA(ImpInterface&& impobj)
{
printf("construct use type %s\n", typeid(impobj).name());
}

private:
};

void test_language()
{
std::vector<AA> a{ AA(10) };
}


输出
construct use type int
construct use type class AA
后崩溃.
在提供了拷贝构造函数的情况下, 却没用使用提供的那一份, 而是选用了模板那一份,执行完后竟然还崩溃了...


3.

#include <cstdio>
#include <type_traits>
#include <typeinfo>
#include <vector>

class AA
{
public:
AA(const AA& a) = default;

template <typename ImpInterface>
AA(ImpInterface&& impobj)
{
printf("construct use type %s\n", typeid(impobj).name());
}

private:
};

void test_language()
{
std::vector<AA> a{ AA(10) };
}


输出
construct use type int
construct use type class AA
后正常结束, 没有崩溃了.

4.

#include <cstdio>
#include <type_traits>
#include <typeinfo>
#include <vector>

class AA
{
public:
AA(const AA& a) = delete;

template <typename ImpInterface>
AA(ImpInterface&& impobj)
{
printf("construct use type %s\n", typeid(impobj).name());
}

private:
};

void test_language()
{
std::vector<AA> a{ AA(10) };
}


编译报错: error C2280: 'AA::AA(const AA &)': attempting to reference a deleted function

5.

#include <cstdio>
#include <type_traits>
#include <typeinfo>
#include <vector>

class AA
{
public:
AA(AA&& a) {};

template <typename ImpInterface>
AA(ImpInterface&& impobj)
{
printf("construct use type %s\n", typeid(impobj).name());
}

private:
};

void test_language()
{
std::vector<AA> a{ AA(10) };
}


编译报错:
error C2280: 'AA::AA(const AA &)': attempting to reference a deleted function

----------------------------------------------------------------------------------------------------------
在有 template <typename ImpInterface> AA(ImpInterface&& impobj) 这种右值引用的模板构造函数的情况下, 到底需要怎么样定义自己的拷贝构造函数呢?
...全文
4801 60 打赏 收藏 转发到动态 举报
写回复
用AI写文章
60 条回复
切换为时间正序
请发表友善的回复…
发表回复
赵4老师 2016-07-04
  • 打赏
  • 举报
回复
引用 59 楼 u012947309 的回复:
[quote=引用 32 楼 zhao4zhong1 的回复:] 巧了! 孔乙己先生说茴香豆的回字也有四中写法。
赵四老师说:“这分我不要了!”[/quote] 个人意见:模板技术比不上代码生成技术。
NoEdUl 2016-07-04
  • 打赏
  • 举报
回复
引用 32 楼 zhao4zhong1 的回复:
巧了! 孔乙己先生说茴香豆的回字也有四中写法。
赵四老师说:“这分我不要了!”
  • 打赏
  • 举报
回复
引用 53 楼 adlay 的回复:
也就是说, 只要进入了调用阶段, 模板进行实例化构造函数就是无条件的了? 而要进入调用阶段, 只要这个类的构造函数没有声明为 delete 就行? 还是有其它条件才能让编译过程进入到调用阶段?
这个问题分成2个问题理解就容易了 编译器 生成的构造、移动函数或删除,不牵扯调用,纯粹根据 类的声明就能够有明确结果。但知道编译器生成了那些函数,函数签名是什么。 调用时模板实例化后,和普通函数、编译器自动生成的函数 进行重载决议。
www_adintr_com 2016-06-30
  • 打赏
  • 举报
回复
引用 52 楼 akirya 的回复:
[quote=引用 51 楼 adlay 的回复:] [quote=引用 50 楼 akirya 的回复:] [quote=引用 48 楼 adlay 的回复:] 前面都不管他, 现在只管这一个点 "之后模板实例化" 这个模板实例化的时候是否实例化出某种形式的拷贝构造函数是根据什么来决定的? 既然你已经否认了是根据之前的操作结果有某一种形式的拷贝构造函数, 那根据的是什么?
标准说是不具备构造函数签名。 A member function template is never instantiated to produce such a constructor signature. 和其他构造函数不是同等地位。 可以认为是构造函数,但不有完全是,是特例。[/quote] 这描述的是实例化出来的那个函数的性质, 也不是在描述什么条件下去实例化呀.[/quote] 不管有没有模板 编译器都会自动生成这些拷贝构造和移动构造。 模板函数只是在调用的时候才推导模板参数,实例化模板的。[/quote] 也就是说, 只要进入了调用阶段, 模板进行实例化构造函数就是无条件的了? 而要进入调用阶段, 只要这个类的构造函数没有声明为 delete 就行? 还是有其它条件才能让编译过程进入到调用阶段?
  • 打赏
  • 举报
回复
引用 55 楼 adlay 的回复:
[quote=引用 54 楼 akirya 的回复:] [quote=引用 53 楼 adlay 的回复:] 也就是说, 只要进入了调用阶段, 模板进行实例化构造函数就是无条件的了? 而要进入调用阶段, 只要这个类的构造函数没有声明为 delete 就行? 还是有其它条件才能让编译过程进入到调用阶段?
这个问题分成2个问题理解就容易了 编译器 生成的构造、移动函数或删除,不牵扯调用,纯粹根据 类的声明就能够有明确结果。但知道编译器生成了那些函数,函数签名是什么。 调用时模板实例化后,和普通函数、编译器自动生成的函数 进行重载决议。[/quote] 那里的意思是代码 4 报错是在进入第三阶段的时候报错的吗? 像这样: 第一阶段, delete 并不是让编译器不生成构造函数, 而是生成一个有 delete 标签的构造函数. 进入第二阶段, 模板照样实例一个 template <AA> AA(const AA& ) 出来. 进入第三阶段, 重载决议阶段, 编译器生成的构造函数获胜, 但是发现这个函数带有 delete 标签, 于是产生编译错误.[/quote] 是这么回事
fefe82 2016-06-30
  • 打赏
  • 举报
回复
拷贝构造/移动构造的概念在决定重载决策(overload resolution)时是没有用到的。 在这一阶段,所有能进入候选集的函数是一视同仁的。 所有的构造函数,包括构造函数模板的实例化结果,总是同时进入候选集的。
fefe82 2016-06-30
  • 打赏
  • 举报
回复
引用 50 楼 akirya 的回复:
[quote=引用 48 楼 adlay 的回复:] 前面都不管他, 现在只管这一个点 "之后模板实例化" 这个模板实例化的时候是否实例化出某种形式的拷贝构造函数是根据什么来决定的? 既然你已经否认了是根据之前的操作结果有某一种形式的拷贝构造函数, 那根据的是什么?
标准说是不具备构造函数签名。 A member function template is never instantiated to produce such a constructor signature. 和其他构造函数不是同等地位。 可以认为是构造函数,但不有完全是,是特例。[/quote] 这句说的应该不是拷贝/移动构造。 它说的模板实例化不能生成如下的签名: A(A a); 这个构造签名是不合法的。 拷贝构造是 A(A& a);
www_adintr_com 2016-06-30
  • 打赏
  • 举报
回复
引用 54 楼 akirya 的回复:
[quote=引用 53 楼 adlay 的回复:] 也就是说, 只要进入了调用阶段, 模板进行实例化构造函数就是无条件的了? 而要进入调用阶段, 只要这个类的构造函数没有声明为 delete 就行? 还是有其它条件才能让编译过程进入到调用阶段?
这个问题分成2个问题理解就容易了 编译器 生成的构造、移动函数或删除,不牵扯调用,纯粹根据 类的声明就能够有明确结果。但知道编译器生成了那些函数,函数签名是什么。 调用时模板实例化后,和普通函数、编译器自动生成的函数 进行重载决议。[/quote] 那里的意思是代码 4 报错是在进入第三阶段的时候报错的吗? 像这样: 第一阶段, delete 并不是让编译器不生成构造函数, 而是生成一个有 delete 标签的构造函数. 进入第二阶段, 模板照样实例一个 template <AA> AA(const AA& ) 出来. 进入第三阶段, 重载决议阶段, 编译器生成的构造函数获胜, 但是发现这个函数带有 delete 标签, 于是产生编译错误.
  • 打赏
  • 举报
回复
引用 43 楼 adlay 的回复:
我的问题是,模板什么时候才实例化出对应的拷贝构造函数, 什么时候又不实例化出对应的拷贝构造函数
规则比较复杂,我也是查了半天才清楚,最重要的一点就是。 模板函数不能阻止编译器生成构造函数、拷贝构造、移动构造。 剩下的就是模板 重载决议。 根据标准规定,AA类生成的拷贝构造参数为 AA(const &&) 移动构造为 AA(AA&&); 1 AA(10) 不符合编译器生成的构造,于是就用模板构造对象,由于AA(10)是右值,那么构造initializer_list<AA>匹配 生成的移动构造函数,vector内使用拷贝构造。 2 由于定义了拷贝构造,那么就不会生成移动构造,移动构造由模板实例化。 3 同2 ,定义了拷贝构造,实现由编译器生成 4 基本同2,存在一个删除的拷贝构造。模板不能生成delete的函数,否则delete关键字无意义,达不到目的。 5 只定义了移动构造,没定义默认的拷贝构造的话,相当于 AA(const AA&)=delete; 模板不能生成拷贝构造。于是编译错误。 7 已经解释过了,右值不能匹配 AA(AA&),所以用的是模板生成的AA(const AA&);
nanjun520 2016-06-29
  • 打赏
  • 举报
回复
大神们 厉害 我是来学习的
www_adintr_com 2016-06-29
  • 打赏
  • 举报
回复
引用 42 楼 akirya 的回复:
[quote=引用 41 楼 akirya 的回复:] [quote=引用 40 楼 adlay 的回复:] [quote=引用 39 楼 akirya 的回复:] 标准规定:有移动构造的话,就不会自动生成拷贝构造函数了。至于原因只能去标准草案的邮件列表里面找了。
你是说, 有移动构造的话, 就不会从模板实例化出拷贝构造函数吗? 我这里问的是模板实例化, 没问生成默认的拷贝构造函数哈. 如果你是上面的意思, 那么在 代码7 中, 有移动构造, 仍然从模板实例化出了一份拷贝构造函数.[/quote] 我理解错误,我说的是没有模板的情况下. std::vector<AA> a{ AA(10) }; 这里的AA(10)是右值,自然无法使用 AA(AA&)这个版本的拷贝构造。 需要AA(const AA&)版的,没有精确匹配,就使用模板版的。[/quote] 更正一下 标准规定,有移动构造的话,编译器就不会生成拷贝构造函数。 跟有没有模板重载没有关系,但模板可以实例化出对应的拷贝构造函数,和编译器生成的不一样。[/quote] 我的问题是,模板什么时候才实例化出对应的拷贝构造函数, 什么时候又不实例化出对应的拷贝构造函数
  • 打赏
  • 举报
回复
引用 41 楼 akirya 的回复:
[quote=引用 40 楼 adlay 的回复:] [quote=引用 39 楼 akirya 的回复:] 标准规定:有移动构造的话,就不会自动生成拷贝构造函数了。至于原因只能去标准草案的邮件列表里面找了。
你是说, 有移动构造的话, 就不会从模板实例化出拷贝构造函数吗? 我这里问的是模板实例化, 没问生成默认的拷贝构造函数哈. 如果你是上面的意思, 那么在 代码7 中, 有移动构造, 仍然从模板实例化出了一份拷贝构造函数.[/quote] 我理解错误,我说的是没有模板的情况下. std::vector<AA> a{ AA(10) }; 这里的AA(10)是右值,自然无法使用 AA(AA&)这个版本的拷贝构造。 需要AA(const AA&)版的,没有精确匹配,就使用模板版的。[/quote] 更正一下 标准规定,有移动构造的话,编译器就不会生成拷贝构造函数。 跟有没有模板重载没有关系,但模板可以实例化出对应的拷贝构造函数,和编译器生成的不一样。
  • 打赏
  • 举报
回复
引用 51 楼 adlay 的回复:
[quote=引用 50 楼 akirya 的回复:] [quote=引用 48 楼 adlay 的回复:] 前面都不管他, 现在只管这一个点 "之后模板实例化" 这个模板实例化的时候是否实例化出某种形式的拷贝构造函数是根据什么来决定的? 既然你已经否认了是根据之前的操作结果有某一种形式的拷贝构造函数, 那根据的是什么?
标准说是不具备构造函数签名。 A member function template is never instantiated to produce such a constructor signature. 和其他构造函数不是同等地位。 可以认为是构造函数,但不有完全是,是特例。[/quote] 这描述的是实例化出来的那个函数的性质, 也不是在描述什么条件下去实例化呀.[/quote] 不管有没有模板 编译器都会自动生成这些拷贝构造和移动构造。 模板函数只是在调用的时候才推导模板参数,实例化模板的。
www_adintr_com 2016-06-29
  • 打赏
  • 举报
回复
引用 50 楼 akirya 的回复:
[quote=引用 48 楼 adlay 的回复:] 前面都不管他, 现在只管这一个点 "之后模板实例化" 这个模板实例化的时候是否实例化出某种形式的拷贝构造函数是根据什么来决定的? 既然你已经否认了是根据之前的操作结果有某一种形式的拷贝构造函数, 那根据的是什么?
标准说是不具备构造函数签名。 A member function template is never instantiated to produce such a constructor signature. 和其他构造函数不是同等地位。 可以认为是构造函数,但不有完全是,是特例。[/quote] 这描述的是实例化出来的那个函数的性质, 也不是在描述什么条件下去实例化呀.
  • 打赏
  • 举报
回复
引用 48 楼 adlay 的回复:
前面都不管他, 现在只管这一个点 "之后模板实例化" 这个模板实例化的时候是否实例化出某种形式的拷贝构造函数是根据什么来决定的? 既然你已经否认了是根据之前的操作结果有某一种形式的拷贝构造函数, 那根据的是什么?
标准说是不具备构造函数签名。 A member function template is never instantiated to produce such a constructor signature. 和其他构造函数不是同等地位。 可以认为是构造函数,但不有完全是,是特例。
www_adintr_com 2016-06-29
  • 打赏
  • 举报
回复
引用 48 楼 adlay 的回复:
[quote=引用 47 楼 akirya 的回复:] [quote=引用 46 楼 adlay 的回复:] 你的结论是, 只要这个类有任意一种参数形式的拷贝构造(不管是默认生成的, 还是用户定义的), 模板就会实例化出其余参数形式的拷贝构造和移动构造. 如果这个类没有任何一种拷贝构造, 模板就不能实例化出拷贝构造和移动构造. 是这样的吗?
不是 编译器先根据用户定义的构造函数情况,生成、不生成或删除 对应的拷贝构造、移动函数。 之后模板实例化,进行重载决议。 [/quote] 前面都不管他, 现在只管这一个点 "之后模板实例化" 这个模板实例化的时候是否实例化出某种形式的拷贝构造函数是根据什么来决定的? 既然你已经否认了是根据之前的操作结果有某一种形式的拷贝构造函数, 那根据的是什么?[/quote] 是只要没有定义成 =delete 就生成吗?
www_adintr_com 2016-06-29
  • 打赏
  • 举报
回复
引用 47 楼 akirya 的回复:
[quote=引用 46 楼 adlay 的回复:] 你的结论是, 只要这个类有任意一种参数形式的拷贝构造(不管是默认生成的, 还是用户定义的), 模板就会实例化出其余参数形式的拷贝构造和移动构造. 如果这个类没有任何一种拷贝构造, 模板就不能实例化出拷贝构造和移动构造. 是这样的吗?
不是 编译器先根据用户定义的构造函数情况,生成、不生成或删除 对应的拷贝构造、移动函数。 之后模板实例化,进行重载决议。 [/quote] 前面都不管他, 现在只管这一个点 "之后模板实例化" 这个模板实例化的时候是否实例化出某种形式的拷贝构造函数是根据什么来决定的? 既然你已经否认了是根据之前的操作结果有某一种形式的拷贝构造函数, 那根据的是什么?
  • 打赏
  • 举报
回复
引用 46 楼 adlay 的回复:
你的结论是, 只要这个类有任意一种参数形式的拷贝构造(不管是默认生成的, 还是用户定义的), 模板就会实例化出其余参数形式的拷贝构造和移动构造. 如果这个类没有任何一种拷贝构造, 模板就不能实例化出拷贝构造和移动构造. 是这样的吗?
不是 编译器先根据用户定义的构造函数情况,生成、不生成或删除 对应的拷贝构造、移动函数。 之后模板实例化,进行重载决议。
引用 46 楼 adlay 的回复:
只定义了拷贝构造的时候呢? 是否也相当于 AA(AA&&) = delete; 了呢?
不相当于AA(AA&&&)=delete,因为没有移动的时候,使用右值构造,就会直接调用拷贝构造,而不是的编译错误。
www_adintr_com 2016-06-29
  • 打赏
  • 举报
回复
引用 45 楼 akirya 的回复:
[quote=引用 43 楼 adlay 的回复:] 我的问题是,模板什么时候才实例化出对应的拷贝构造函数, 什么时候又不实例化出对应的拷贝构造函数
规则比较复杂,我也是查了半天才清楚,最重要的一点就是。 模板函数不能阻止编译器生成构造函数、拷贝构造、移动构造。 剩下的就是模板 重载决议。 根据标准规定,AA类生成的拷贝构造参数为 AA(const &&) 移动构造为 AA(AA&&); 1 AA(10) 不符合编译器生成的构造,于是就用模板构造对象,由于AA(10)是右值,那么构造initializer_list<AA>匹配 生成的移动构造函数,vector内使用拷贝构造。 2 由于定义了拷贝构造,那么就不会生成移动构造,移动构造由模板实例化。 3 同2 ,定义了拷贝构造,实现由编译器生成 4 基本同2,存在一个删除的拷贝构造。模板不能生成delete的函数,否则delete关键字无意义,达不到目的。 5 只定义了移动构造,没定义默认的拷贝构造的话,相当于 AA(const AA&)=delete; 模板不能生成拷贝构造。于是编译错误。 7 已经解释过了,右值不能匹配 AA(AA&),所以用的是模板生成的AA(const AA&);[/quote] 你的结论是, 只要这个类有任意一种参数形式的拷贝构造(不管是默认生成的, 还是用户定义的), 模板就会实例化出其余参数形式的拷贝构造和移动构造. 如果这个类没有任何一种拷贝构造, 模板就不能实例化出拷贝构造和移动构造. 是这样的吗?
引用 45 楼 akirya 的回复:
5 只定义了移动构造,没定义默认的拷贝构造的话,相当于 AA(const AA&)=delete;
只定义了拷贝构造的时候呢? 是否也相当于 AA(AA&&) = delete; 了呢?
  • 打赏
  • 举报
回复
引用 38 楼 adlay 的回复:
[quote=引用 37 楼 ri_aje 的回复:] 它们不叫复制构造函数,它们只是刚好能匹配复制构造的重载解析。 上面两条我已经说了好几遍了,你要不理解它们的区别,我也爱莫能助了。 最后再解释一次,在你所有的例子里,编译器都是一样的逻辑。 如果需要生成构造函数,就生成,如果不需要,就使用用户提供的。 同时根据调用参数实例化函数模板,然后进行重载解析,选定谁就是谁。
好吧, 你叫他 "匹配复制构造的重载解析" 就叫 "匹配复制构造的重载解析" 吧. 我不关心他叫什么, 也不关心他和复制构造函数的区别... 我的问题在于编译器什么时候会用模板来生成这个 "匹配复制构造的重载解析"? 为什么在 代码 5 中编译器不用模板来生成一个形如 AA::AA(const AA &) 的 "匹配复制构造的重载解析" 而在代码7 中会生成? 另外, 一点哲学上的个人观点, 标准不是问题的终极答案, 制定标准的时候肯定是经过各种考量的, 所以,一样可以问"标准为什么允许?" 的. 如果不用整天把标准挂在嘴边, 可以简略的问成 "为什么允许?", 这时候期待的答案通常不是 "标准允许."[/quote] 标准规定:有移动构造的话,就不会自动生成拷贝构造函数了。至于原因只能去标准草案的邮件列表里面找了。
加载更多回复(40)

64,637

社区成员

发帖
与我相关
我的任务
社区描述
C++ 语言相关问题讨论,技术干货分享,前沿动态等
c++ 技术论坛(原bbs)
社区管理员
  • C++ 语言社区
  • encoderlee
  • paschen
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告
  1. 请不要发布与C++技术无关的贴子
  2. 请不要发布与技术无关的招聘、广告的帖子
  3. 请尽可能的描述清楚你的问题,如果涉及到代码请尽可能的格式化一下

试试用AI创作助手写篇文章吧