为move调用转换构造函数,但未为copy调用转换构造函数
Conversion constructor called for move but not for copy
使用以下代码:
template <class T> class Test {
T _temp;
public:
Test() {
std::cout << "Test()" << std::endl;
};
template <class T2> Test(Test<T2> const &test) {
std::cout << "template <class T2> Test(Test<T2> const &test)" << std::endl;
};
template <class T2> Test(Test<T2> &&test) {
std::cout << "template <class T2> Test(Test<T2> &&test)" << std::endl;
};
};
使用下面的测试代码:
Test<int> testInt;
Test<float> testFloat(testInt);
Test<float> testFloat2(std::move(testInt));
std::cout << "----------" << std::endl;
Test<int> testInt2;
Test<int> testInt3(testInt2);
Test<int> testInt4(std::move(testInt2));
产生如下输出:
Test()
template <class T2> Test(Test<T2> const &test)
template <class T2> Test(Test<T2> &&test)
----------
Test()
当使用相同类型时,使用默认的复制和移动构造函数代替转换构造函数。
但是如果我在类中添加默认复制构造函数:
Test(Test const &test) = default;
输出如下:
Test()
template <class T2> Test(Test<T2> const &test)
template <class T2> Test(Test<T2> &&test)
----------
Test()
template <class T2> Test(Test<T2> &&test)
move转换构造函数即使对相同的类型也被调用,为什么?
是否有办法统一复制和转换构造函数以避免重复代码?
添加隐式生成(=编译器生成)移动构造函数的规则非常保守:只在安全的情况下添加一个。如果用户已经定义了复制构造函数,编译器不能认为添加一些简单的编译器生成的移动构造函数仍然是安全的,因此不会添加移动构造函数。实际上,声明它足以防止生成move构造函数,IIRC允许仅复制类型。
构造函数模板既不被认为是复制构造函数,也不被认为是移动构造函数,所以它们不阻止它们的隐式生成。
当然,如果从未声明move构造函数,则重载解析可以选择另一个构造函数。考虑这个例子:#include <iostream>
#include <utility>
struct loud
{
loud() { std::cout << "default ctorn"; }
loud(loud const&) { std::cout << "copy ctorn"; }
loud(loud&&) { std::cout << "move ctorn"; }
};
struct foo
{
loud l;
};
struct bar
{
loud l;
bar() = default;
bar(bar const&) = default;
};
int main()
{
foo f0;
foo f1(f0);
foo f2(std::move(f0));
std::cout << "--------------n";
bar b0;
bar b1(b0);
bar b2(std::move(b0));
}
输出:<>之前默认的男星复制男星移动男星--------------默认的男星复制男星复制男星之前这里,foo
将隐式声明默认、复制和移动构造函数,而bar
将不会隐式声明移动构造函数,因为它有用户声明的复制构造函数。
在第二种情况下,没有隐式声明的move构造函数,因此Test<int> testInt4(std::move(testInt2))
的重载解析更倾向于构造函数模板而不是复制构造函数,因为前者接受一个不太符合cv的引用。
为了减少代码重复,您可以委托构造:
template <class T>
class Test {
T _temp;
struct tag {};
public:
Test() {
std::cout << "Test()" << std::endl;
};
Test(Test const& test)
: Test(test, tag{})
{}
Test(Test&& test)
: Test(std::move(test), tag{})
{}
template <class T2> Test(Test<T2> const &test, tag = {}) {
std::cout << "template <class T2> Test(Test<T2> const &test)" << std::endl;
};
template <class T2> Test(Test<T2> &&test, tag = {}) {
std::cout << "template <class T2> Test(Test<T2> &&test)" << std::endl;
};
};
顺便说一下,你也可以通过使用一个完美的转发构造函数模板而不是两个构造函数模板来减少一些样板文件:
使用这个特性:
template<class T>
struct is_Test : std::false_type {};
template<class T>
struct is_Test<Test<T>> : std::true_type {};
你可以定义:
template <class T2,
class = typename std::enable_if<is_Test<T2>::value>::type>
Test(T2&& test, tag = {}) {
std::cout << "template <class T2> Test(T2&& test)" << std::endl;
};
相关文章:
- 为什么在使用转换构造函数赋值后调用C++类的析构函数?
- enable_if转换构造函数(静态强制转换,is_base_of)
- 为什么我需要在转换构造函数上引用 this->?
- 为什么在使用转换构造函数编译代码时需要 const 复制构造函数?
- 为什么不对转换构造函数进行隐式强制转换?
- 转换构造函数和运算符都存在且涉及显式性时的行为
- C++ 03 类模板 这是转换构造函数还是转换运算符?以及如何声明解决此问题的方法
- 使用完美转发的模板转换构造函数
- 转换构造函数的参数可以隐式转换吗?
- std::变量转换构造函数行为
- 隐式用户定义的转换不起作用,因为在编译C 时无法识别运算符和转换构造函数
- 为什么不调用模板类中的转换构造函数?
- 转换构造函数的隐式参数
- 转换构造函数:您如何解释C 中给定不同参数的函数
- C++变体:为什么转换构造函数需要大小.(类型)为非零
- C++变体用bool转换构造函数
- 在重载解析期间调用转换运算符,而不是在 c++17 中转换构造函数
- 为什么在没有赋值运算符的情况下调用转换构造函数
- 如何将转换构造函数与指针一起使用?
- 为什么在调用隐式类型转换构造函数之后直接是驱动器