c++ - 模板构造函数意外触发移动构造函数(std::function 和 mini case)

标签 c++ c++11 templates constructor rvalue-reference

在下面的代码中,为什么会调用 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,但我无法理解它。 谁能给我解释一下?

此外,在我的真实情况下(用 std::function 替换类 I),有没有办法构造包装类 FF 而无需内部仿函数 F 移动两次?

最佳答案

I 构造函数的模板化版本通过值而不是右值引用获取其参数。按值参数是通过调用你想知道的移动构造函数构造的。

您需要一个“通用引用”样式的构造函数,它将其参数声明为右值引用。这是 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。如果您希望参数是一个引用,您必须明确指定一个引用类型作为您的参数。

关于c++ - 模板构造函数意外触发移动构造函数(std::function 和 mini case),我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/40928333/

相关文章:

c++ - 将可变参数模板列表中的整数值分配给静态常量 std::array 成员

c++ - 终止函数模板递归

C++ 将管道读入字符串

c++ - 这个问题有更好的数据结构和算法选择吗?

javascript - 如何将带有 django 标签的 javascript 代码加载到我的 django 模板中

c++ - vector<unique_ptr<myclass>> 元素之间的指针

c++ - void 表达式的无效使用 - C++

c++ - 如何使用特定版本的 zlib 在 ubuntu 上构建 openssl?

c++ - 在基于 Visual Studio MFC 的应用程序中禁用事件处理程序

c++ - Win32 : Storing Multi-Line Text in a Buffer