从类型std::函数传递变量失败,尽管调用方期望的类型完全相同


由于某些原因,我无法将值传递给具有确切类型的方法。

但是,如果我使用decltype本身来强制转换我希望传递的变量,它会以某种方式起作用(?(也许有人知道原因是什么。

此外,当我将lambda块传递给std::函数时,它不需要任何强制转换,尽管它们并不完全相同。

下面的例子展示了我的发现:

#include <functional>
class A {
public:
A(std::function<void(std::string&, int)> &&f): _f(f) { }
std::function<void(std::string&,int)> _f;
};
class B : public A {
public:
//B(std::function<void(std::string&, int)> &&f) : A(f) { } --> fail on casting error, why ?
B(std::function<void(std::string&, int)> &&f) : A((decltype(f)) f) { }
};

class C {
public:
C(std::function<void(std::string&,int)> f) {
//b = new B(f); --> this one trigger casting error, why ? 
b = new B((decltype(f)) f);
}
B *b;
};
int main(int argc, const char * argv[]) {
// this works for some reason, even though C c'tor expect std::function ... why ? 
C c([=](std::string&a, int b) {
printf("%s %dn", a.c_str(), b);
});
}

如果您得到一个右值ref,并希望将此参数作为参数传递给您调用的任何函数,则必须使用std::move将其保留为右值ref。

解释为什么和如何使用std::move,你能在这里找到吗

BTW:我不知道为什么你需要在函数/构造函数中重新评估ref,但这在你的真实代码中可能很有用。

你的代码是这样的:

class A {
public:
A(std::function<void(std::string&, int)> &&f): _f(std::move(f)) { }
std::function<void(std::string&,int)> _f;
};
class B : public A {
public:
B(std::function<void(std::string&, int)> &&f) : A(std::move(f)) { }
};

class C {
public:
// ATTENTION: Here you use not an rvalue ref, so you create a copy
// which later will be moved. Can be ok, dependent on what you want
// to achieve
C(std::function<void(std::string&,int)> f):b( new B(std::move(f))) { }
B *b;
};
int main() {
C c([=](std::string&a, int b) {
printf("%s %dn", a.c_str(), b);
});
}

位于:B(std::function<void(std::string&, int)> &&f) : A((decltype(f)) f) { }

由于f有一个名称,它是一个左值,但A的构造函数需要一个

右值这里的CCD_ 7等价于CCD_。

问题是AB的构造函数在传递左值时需要一个右值引用。让我们考虑一下B的构造函数:

B(std::function<void(std::string&, int)> &&f) : A((decltype(f)) f) { }

由于参数f被声明为右值引用,因此它只能绑定到调用方的右值。换句话说,将左值作为参数传递给构造函数是非法的。

但是,函数体(包括构造函数的初始值设定项列表(f上下文中的此参数是一个左值。这意味着它不能直接传递给A的构造函数(它也需要一个右值(。(decltype(f))转换所做的是将f转换为右值引用。更传统的方法是使用std::move:

B(std::function<void(std::string&, int)> &&f) : A(std::move(f)) { }

通过调用std::move,您可以在不复制的情况下将f左值强制转换为右值引用。

在其他地方,你也有同样的问题——你试图传递一个左值作为一个右值参数。使用std::move也可以解决此问题。

现在,到这个部分:

// this works for some reason, even though C c'tor expect std::function ... why ? 
C c([=](std::string&a, int b) {
printf("%s %dn", a.c_str(), b);
});

这是因为std::function有一个来自函数对象的隐式转换构造函数,lambda函数就是这个对象。此代码隐式构造了一个临时std::function<void(std::string&, int)>对象,该对象将lambda复制到其内部存储中。然后,该临时性被传递给C的构造函数(假设发生了复制省略,否则该临时性的副本将被传递给构造函数(。

//由于某些原因,即使C期望std::函数。。。为什么?

C c([=](std::string&a, int b) {
printf("%s %dn", a.c_str(), b);
});

由于构造函数不是explicit,编译器会寻找一个可以将传递值隐式转换为的构造函数。因此,可以通过std::function构造函数之一将传递值(即lambda(转换为std::function

//b = new B(f); --> this one trigger casting error, why ? 
b = new B((decltype(f)) f);

B的构造函数需要一个右值,但f是一个左值。这导致了错误。但是,强制转换会创建一个临时的,即副本,它是一个右值。

//B(std::function<void(std::string&, int)> &&f) : A(f) { } --> fail on casting error, why ?
B(std::function<void(std::string&, int)> &&f) : A((decltype(f)) f) { }

尽管f绑定到右值,但由于f有名称,因此它本身就是一个左值。将其强制转换为右值(decltype(f)) f)(会将其转换回右值。这就是std::move所做的事情。

相关内容

最新更新