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

时间:2016-12-02 09:05:07

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

在下面的代码中,为什么调用F的移动ctor?

当我试图包装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]调用的意外移动ctor,但我无法理解它。 有人能解释一下吗?

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

1 个答案:

答案 0 :(得分:1)

I构造函数的模板化版本按值而不是rvalue引用获取其参数。 by-value参数是通过调用您想知道的移动构造函数构建的。

您需要一个'通用引用'样式构造函数,它将其参数声明为右值引用。这是允许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&& 。但是,这并不是模板推导在这种情况下的工作方式:编译器只会推导出'衰变'类型,即F。如果希望参数作为参考,则必须明确指定引用类型作为参数。