4

我正在使用 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(参见演示)。谁能解释为什么布尔运算符在包含一个空的、不可调用的函数指针时返回 true ?nullptr并请提及一个解决方法来检查std::function

注意:我已经尝试检查目标是否为空(使用foo.target<void*>() == nullptr)而不是使用布尔运算符,但似乎无论函数对象包含什么,目标始终为空(即使函数对象非常好被调用)。

4

2 回答 2

5

对我来说似乎是一个错误。首先,这是一个简化的示例,它不玩任何有演员表的游戏:

#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 要求: FCopyConstructible。对于参数类型和返回类型f应为Callable(20.8.11.2) 。的复制构造函数和析构函数不得抛出异常。8后置条件 如果以下任何一项成立:ArgTypesRA
!*this

  • f是一个NULL函数指针。
  • fNULL指向成员的指针。
  • F是函数类模板的一个实例,并且!f
于 2013-10-24T23:39:24.677 回答
4

我不认为代码在做你认为它做的事情。这一行:

foo = reinterpret_cast<void(*)()>(Test());

表示您收到void*来自Test(). 然后,您继续reinterpret_cast将此指向对象指针转换为指向函数的指针。这是不允许的,因此代码会产生未定义的行为,因此编译器的任何输出都是有效的。

标准的相关部分是

5.2.10 重新解释演员表 [expr.reinterpret.cast]

8有条件地支持将函数指针转换为对象指针类型或反之亦然。这种转换的含义是实现定义的,除非实现支持双向转换,将一种类型的纯右值转换为另一种类型并返回,可能具有不同的 cv 限定,应产生原始指针值。

9空指针值 (4.10) 转换为目标类型的空指针值。[注:类型的空指针常量std::nullptr_t不能转换为指针类型,整型的空指针常量不一定转换为空指针值。——尾注]

4.10 指针转换 [conv.ptr]

1空指针常量是整数类型的整数常量表达式 (5.19) 纯右值,其计算结果为零或类型的纯右值std::nullptr_t。空指针常量可以转换为指针类型;结果是该类型的空指针值 ,并且可以与对象指针或函数指针类型的所有其他值区分开来

(强调我的)

这是一个简化的测试用例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;
}

活生生的例子

于 2013-10-24T23:09:41.280 回答