模板构造函数意外触发移动构造函数(std::function 和 mini case)
Move constructor trigger unexpectedly by template constructor (std::function and mini case)
在下面的代码中,为什么会调用 F 的移动构造函数?
当我试图包装 std::function 时,我 运行 陷入了这种情况,并惊讶地发现我的内部仿函数在构造过程中被移动了两次。
#include <iostream>
using namespace std;
struct F {
F() { cout << "F()" << endl; }
F(const F &) { cout << "F(const F &)" << endl; }
F(F &&) {
cout << "F(F &&)" << endl;
}
void operator()() { cout << "F::()()" << endl;}
};
struct I {
#if 0
// F's move ctor not invoked
I(F &&) { }
#else
// F's move ctor invoked, why?
template<typename FT, typename = void>
I(FT) { } // Simulate std::function ctor prototype
#endif
};
struct FF {
FF(F &&f) : _impl(std::move(f)) {} // [line1] Debugger show source here
I _impl;
};
int main() {
FF ff = F();
(void)ff;
cout << "----" << endl;
return 0;
}
(使用 g++ 4.8.x-5.3.x)
gdb 显示 [line1] 调用了意外的 move ctor,但我无法理解它。
谁能给我解释一下?
此外,在我的真实情况下(将class I 替换为std::function),有什么方法可以构造包装器class FF 而无需将内部函子F 移动两次?
您的 I
构造函数的模板化版本通过值而不是右值引用获取其参数。按值参数是通过调用你想知道的移动构造函数构造的。
您需要一个 'universal reference' 样式的构造函数,它将其参数声明为右值引用。这是 std::function
和其他构造所使用的允许完美转发的习语。
所以改变:
template<typename FT, typename = void>
I(FT) { } // Simulate std::function ctor prototype
收件人:
template<typename FT, typename = void>
I(FT&&) { } // Simulate std::function ctor prototype
详细说明一下:您可能认为通过简单地在调用中提供具有右值引用类型的值,编译器会将类型 FT
实例化为右值引用,即 F&&
.然而,在这种情况下,模板推导并不是这样工作的:编译器只会推导 'decayed' 类型,即在这种情况下为 F
。如果您希望参数成为引用,则必须明确指定引用类型作为参数。
在下面的代码中,为什么会调用 F 的移动构造函数?
当我试图包装 std::function 时,我 运行 陷入了这种情况,并惊讶地发现我的内部仿函数在构造过程中被移动了两次。
#include <iostream>
using namespace std;
struct F {
F() { cout << "F()" << endl; }
F(const F &) { cout << "F(const F &)" << endl; }
F(F &&) {
cout << "F(F &&)" << endl;
}
void operator()() { cout << "F::()()" << endl;}
};
struct I {
#if 0
// F's move ctor not invoked
I(F &&) { }
#else
// F's move ctor invoked, why?
template<typename FT, typename = void>
I(FT) { } // Simulate std::function ctor prototype
#endif
};
struct FF {
FF(F &&f) : _impl(std::move(f)) {} // [line1] Debugger show source here
I _impl;
};
int main() {
FF ff = F();
(void)ff;
cout << "----" << endl;
return 0;
}
(使用 g++ 4.8.x-5.3.x)
gdb 显示 [line1] 调用了意外的 move ctor,但我无法理解它。 谁能给我解释一下?
此外,在我的真实情况下(将class I 替换为std::function),有什么方法可以构造包装器class FF 而无需将内部函子F 移动两次?
您的 I
构造函数的模板化版本通过值而不是右值引用获取其参数。按值参数是通过调用你想知道的移动构造函数构造的。
您需要一个 'universal reference' 样式的构造函数,它将其参数声明为右值引用。这是 std::function
和其他构造所使用的允许完美转发的习语。
所以改变:
template<typename FT, typename = void>
I(FT) { } // Simulate std::function ctor prototype
收件人:
template<typename FT, typename = void>
I(FT&&) { } // Simulate std::function ctor prototype
详细说明一下:您可能认为通过简单地在调用中提供具有右值引用类型的值,编译器会将类型 FT
实例化为右值引用,即 F&&
.然而,在这种情况下,模板推导并不是这样工作的:编译器只会推导 'decayed' 类型,即在这种情况下为 F
。如果您希望参数成为引用,则必须明确指定引用类型作为参数。