为什么 boost::bind 存储传入类型的参数,而不是函数期望的类型
Why does boost::bind store arguments of the type passed in rather than of the type expected by the function?
我最近在使用boost::bind时在代码中遇到了一个错误。
从提升::绑定文档:
绑定采用的参数由返回的函数对象在内部复制和保存。
我假设所持有的副本的类型是基于函数的签名。 但是,它实际上基于传入的值的类型。
在我的例子中,发生了隐式转换,将绑定表达式中使用的类型转换为函数接收的类型。 我期望这种转换发生在绑定的站点,但是当使用生成的函数对象时会发生。
回想起来,我应该能够从以下事实中弄清楚这一点:当类型仅在调用站点而不是绑定站点不兼容时,使用 boost::bind 会出错。
我的问题是:为什么 boost::bind 以这种方式工作?
- 它似乎给出了更糟糕的编译器错误消息
- 当发生隐式转换并且对函子有多个调用时,它似乎效率较低
但考虑到Boost的设计有多好,我猜是有原因的。 它的行为是从 std::bind1st/bind2nd 继承的吗? 是否有一个微妙的原因为什么这很难/不可能实现? 完全是别的什么?
为了测试第二种理论,我写了一个似乎有效的小代码片段,但很可能有一些绑定的特性我没有考虑到,因为它只是一个片段:
namespace b = boost;
template<class R, class B1, class A1>
b::_bi::bind_t<R, R (*) (B1), typename b::_bi::list_av_1<B1>::type>
mybind(R (*f) (B1), A1 a1)
{
typedef R (*F) (B1);
typedef typename b::_bi::list_av_1<B1>::type list_type;
return b::_bi::bind_t<R, F, list_type> (f, list_type(B1(a1)));
}
struct Convertible
{
Convertible(int a) : b(a) {}
int b;
};
int foo(Convertible bar)
{
return 2+bar.b;
}
void mainFunc()
{
int x = 3;
b::function<int()> funcObj = mybind(foo, x);
printf("val: %dn", funcObj());
}
因为函子可能支持多个重载,这可能会产生不同的行为。即使您知道所有参数时可以解决此签名(我不知道标准C++是否可以保证此功能),bind
也不知道所有参数,因此绝对无法提供。因此,bind
没有必要的信息。
编辑:只是为了澄清,考虑
struct x {
void operator()(int, std::vector<float>);
void operator()(float, std::string);
};
int main() {
auto b = std::bind(x(), 1); // convert or not?
}
即使您要反思结构并了解其重载,仍然无法确定是否需要将1
转换为浮点数。
在不同情况下,您需要在调用站点处理参数。
第一个这样的例子是调用一个成员函数,您可以在对象的副本(boost::bind( &std::vector<int>::push_back, myvector)
)上调用成员,这很可能是您不想要的,或者您需要传递一个指针,绑定器将根据需要取消引用指针(boost::bind( &std::vector<int>::push_back, &myvector )
) --请注意,这两个选项在不同的程序中都有意义
另一个重要的用例是通过引用函数来传递参数。 bind
将复制执行等效的按值传递调用。该库提供了通过辅助函数 ref
和 cref
包装参数的选项,这两个函数都存储指向要传递的实际对象的指针,并且在调用位置取消引用指针(通过隐式转换)。如果在绑定时执行到目标类型的转换,则无法实现。
绑定必须与任何可调用的实体一起使用,无论是函数指针、std::function<>
,还是您自己的函子struct
operator()
。这使得可以使用 ()
调用的任何类型上的绑定泛型。即 Bind 对函子的隐含要求只是它可以与()
一起使用
如果 bind 要存储函数参数类型,则必须以某种方式推断它们作为类型参数传入的任何可调用实体。这显然不会那么通用,因为如果不依赖用户指定某种typedef
(例如),就不可能推断传入结构类型的operator()
的参数类型。因此,对函子(或概念)的要求不再是具体/简单的。
我不完全确定这是原因,但这是一个问题。
编辑:正如DeadMG在另一个答案中提到的另一点,重载即使对于标准函数指针也会产生歧义,因为编译器将无法解析函子类型。通过存储您提供的绑定类型并使用 ()
,也可以避免此问题。
一个很好的例子是将"std::future"绑定到一些采用普通类型的普通函数:
假设我想以一种令人难以置信的异步方式使用一个普通的 f(x,y) 函数。也就是说,我想像"f(X.get(),Y.get())"一样称呼它。这是有充分理由的 - 我可以调用该行,一旦两个输入都可用,f 的逻辑就会运行(我不需要单独的代码行进行连接)。为此,我需要以下内容:
1)我需要支持隐式转换"std::future
operator T() { return get(); }
2)接下来,我需要绑定我的泛型函数以隐藏其所有参数
// Hide the parameters
template<typename OUTPUT, typename... INPUTS>
std::function<OUTPUT()> BindVariadic(std::function<OUTPUT(INPUTS...)> f,
INPUTS&&... in)
{
std::function<OUTPUT()> stub = std::bind( f, std::forward<INPUTS>(in)...);
return stub;
}
使用 std::bind 在调用时执行"std::function
还有其他用例也迫使人们做出这种设计选择。这个用于异步处理的精心设计的产品正是我个人熟悉的。
- 特征::矩阵<双精度,1,3> 结构类型函数中的返回类型函数
- 将C++子类成员函数(虚拟实现)传递给 C 类型函数指针
- C++ 这里有一个返回 (24) 的布尔返回类型函数
- 使用SFINAE来检测void返回类型函数的存在
- 使用此类型函数有什么优势
- 为什么此函数通过类型函数指针调用后,呼叫明智地行为
- 如何使用无类型函数指针调用C++成员函数
- 模板返回类型函数如何在C++中工作
- 具有通用类型函数的动态库[C ]
- 如何在返回类型函数模板的专用化中使用派生类型?( "couldn't infer template argument" )
- Bon appetit :从 int 返回类型函数在 main() 中打印字符串
- 对于需要其他模板参数的类型函数的部分模板专业化
- c++错误的参数类型-函数指针
- 延迟评估模板类型函数
- 在引用或指针返回类型函数上输入
- 具有指针数据类型的非类型函数模板参数
- STL中使用的C++自定义比较类型(函数谓词与较少结构)
- C++模板基类的非类型函数模板的 using 声明
- 字符串到类型函数,模板专用化使调用统一
- 自由类型函数可以接受 Unicode 文件名吗?