我正在使用C ++ 11库中的标准函数包装器,我看到它的布尔运算符有一些奇怪的行为。如果我创建一个std::function
对象,则布尔运算符返回false。如果我将nullptr
分配给对象并再次检查,则仍然如此。当我为它分配一个我已经转换为函数指针的void指针时出现问题。请考虑以下程序:
#include <functional>
#include <iostream>
void* Test() {
return nullptr;
}
int main(int argc, char* argv[]) {
std::function<void()> foo;
std::cout << !!foo << std::endl;
foo = nullptr;
std::cout << !!foo << std::endl;
foo = reinterpret_cast<void(*)()>(Test());
std::cout << !!foo << std::endl;
return 0;
}
我期望输出为0 0 0
但结果为0 0 1
(请参阅demo)。任何人都可以解释为什么布尔运算符在包含空的,不可调用的函数指针时返回true?另请提及在nullptr
std::function
的解决方法
注意:我已经尝试检查目标是否为空(使用foo.target<void*>() == nullptr
)而不是使用布尔运算符,但似乎无论函数对象包含什么,目标始终为null(即使函数对象完全正常被调用也是如此)。
答案 0 :(得分:5)
对我来说看起来像个错误。首先,这是一个simplified example,它不会播放任何带有强制转换的游戏:
#include <functional>
#include <iostream>
typedef void (*VF)();
VF Test() {
return nullptr;
}
int main(int argc, char* argv[]) {
std::function<void()> foo(Test());
std::cout << !!foo << std::endl;
return 0;
}
它仍然用GCC打印1。它不应该:
<强> 20.8.11.2.1 强>
template<class F> function(F f);
template <class F, class A> function(allocator_arg_t, const A& a, F f);
7 需要:F
应为CopyConstructible
。f
为Callable
(20.8.11.2) 参数类型ArgTypes
和返回类型R
。复制构造函数和A
的析构函数不得抛出异常 8 后置条件:!*this
如果有下列任何一种情况:
f
是NULL
函数指针。f
是指向成员的NULL
指针。F
是函数类模板的一个实例,!f
答案 1 :(得分:4)
我不认为代码正在做你认为它做的事情。这一行:
foo = reinterpret_cast<void(*)()>(Test());
表示您从void*
收到Test()
。然后,您将此指针对象 reinterpret_cast
转到指向函数的指针。这是不允许的,因此代码会产生未定义的行为,因此编译器的任何输出都是有效的。
标准的相关部分是
5.2.10重新解释cast [expr.reinterpret.cast]
8 有条件地支持将函数指针转换为对象指针类型,反之亦然。这种转换的含义是实现定义的,除非实现支持两个方向的转换,将一种类型的prvalue转换为另一种类型并返回,可能具有不同的cv-限定,将产生原始指针值。 / p>
9 空指针值(4.10)将转换为目标类型的空指针值。 [注意:类型
std::nullptr_t
的空指针常量不能转换为指针类型,并且整数类型的空指针常量不一定转换为空指针值。 - 结束记录]
和
4.10指针转换[conv.ptr]
1 空指针常量是整数类型的整数常量表达式(5.19)prvalue,其求值为零或类型为
std::nullptr_t
的prvalue。空指针常量可以转换为指针类型;结果是该类型的空指针值 ,可与对象指针或函数指针类型的每个其他值区分开来。
(强调我的)
这是一个简化的测试用例,将std::function
(及其可能的错误)排除在等式之外:
#include <iostream>
int main() {
using fp_t = void(*)();
void* vn = nullptr;
fp_t foo = reinterpret_cast<fp_t>(vn); // GCC: warning, Clang: silence
//fp_t foo = reinterpret_cast<fp_t>(nullptr); // error (GCC and Clang!)
std::cout << !!foo << std::endl;
}