考虑以下示例。让我们创建一个模板类来模拟具有非显式模板转换构造函数的类。
#include <iostream>
// Hypothetical overloaded constructor
template <class T>
struct Foo {
template <class F>
Foo(F&& f ) { std::cout << "Initialization of Foo \n"; }
Foo(const Foo& ) { std::cout << "Copy of Foo\n"; }
Foo(Foo&& ) { std::cout << "Move of Foo\n"; }
};
void bar( Foo<int> f ) {}
int main()
{
int a = 0;
std::cout << "1st case\n";
bar(a);
std::cout << "2nd case\n";
bar(Foo<int>(a)); // incorrect behaviour
}
输出将是
1st case
Initialization of Foo
2nd case
Initialization of Foo
在这两种情况下,模板都会劫持控制权!在这种情况下,您实际上不能使用复制\移动构造函数。避免它的最简单方法是显式转换。
#include <iostream>
// Analog of standard's constructor idiom
template <class T>
struct Foo2 {
template <class F>
explicit Foo2(F&& f ) { std::cout << "Initialization of Foo2 \n"; }
Foo2(const Foo2& ) { std::cout << "Copy of Foo2\n"; }
Foo2(Foo2&& ) { std::cout << "Move of Foo2\n"; }
};
void bar2( Foo2<int> f ) {}
int main()
{
int a = 0;
Foo2<int> f{a};
std::cout << "\nProper case 1\n";
// bar2(a); - can't do that
bar2(Foo2<int>(a));
std::cout << "Proper case 2\n";
bar2(f);
return 0;
}
输出:
Initialization of Foo2
Proper case 1
Initialization of Foo2
Proper case 2
Copy of Foo2
std::function
是可复制的,同时std::packaged_task
必须定义自定义移动构造函数和删除复制构造函数。
在尝试传递std::function
时,在这两种情况下都不会发生任何 BAD,复制和移动构造函数很可能对函数指针或对可调用对象的引用进行操作,std::function
旨在对任何兼容的可调用对象(包括其自身)进行操作。
如果您尝试这样做 std::packaged_task
,则转换构造函数可能会做错事或可能无法编译,因为它无法使用自己的类的实例。一个看起来像复制的语句(但实际上是.. 移动?赋值?)是可能的。