【问题标题】:Failure when casting NULL/nullptr to std::function<>&将 NULL/nullptr 转换为 std::function<>& 时失败
【发布时间】:2014-12-07 14:22:49
【问题描述】:

此代码无法在 VC2013 中构建:(编辑:我不是在问为什么它无法构建)

#include <functional>

struct MyStruct
{
    std::function<void()> m_Func;
    MyStruct( const std::function<void()>& func) :  m_Func(func)   {}
};


int main()
{
    MyStruct rc( NULL );
    return 0;
}

有错误:

1>c:\program files (x86)\microsoft visual studio 12.0\vc\include\xrefwrap(283): error C2064: term does not evaluate to a function taking 0 arguments
1>          c:\program files (x86)\microsoft visual studio 12.0\vc\include\functional(228) : see reference to function template instantiation '_Ret std::_Callable_obj<int,false>::_ApplyX<_Rx,>(void)' being compiled
1>          with
1>          [
1>              _Ret=void
1>  ,            _Rx=void
1>          ]
1>          c:\program files (x86)\microsoft visual studio 12.0\vc\include\functional(228) : see reference to function template instantiation '_Ret std::_Callable_obj<int,false>::_ApplyX<_Rx,>(void)' being compiled
1>          with
1>          [
1>              _Ret=void
1>  ,            _Rx=void
1>          ]
1>          c:\program files (x86)\microsoft visual studio 12.0\vc\include\functional(226) : while compiling class template member function 'void std::_Func_impl<_MyWrapper,_Alloc,_Ret,>::_Do_call(void)'
1>          with
1>          [
1>              _Alloc=std::allocator<std::_Func_class<void,>>
1>  ,            _Ret=void
1>          ]
1>          c:\program files (x86)\microsoft visual studio 12.0\vc\include\functional(495) : see reference to class template instantiation 'std::_Func_impl<_MyWrapper,_Alloc,_Ret,>' being compiled
1>          with
1>          [
1>              _Alloc=std::allocator<std::_Func_class<void,>>
1>  ,            _Ret=void
1>          ]
1>          c:\program files (x86)\microsoft visual studio 12.0\vc\include\functional(396) : see reference to function template instantiation 'void std::_Func_class<_Ret,>::_Do_alloc<_Myimpl,_Ty,_Alloc>(_Fty &&,_Alloc)' being compiled
1>          with
1>          [
1>              _Ret=void
1>  ,            _Ty=int
1>  ,            _Alloc=std::allocator<std::_Func_class<void,>>
1>  ,            _Fty=int
1>          ]
1>          c:\program files (x86)\microsoft visual studio 12.0\vc\include\functional(396) : see reference to function template instantiation 'void std::_Func_class<_Ret,>::_Do_alloc<_Myimpl,_Ty,_Alloc>(_Fty &&,_Alloc)' being compiled
1>          with
1>          [
1>              _Ret=void
1>  ,            _Ty=int
1>  ,            _Alloc=std::allocator<std::_Func_class<void,>>
1>  ,            _Fty=int
1>          ]
1>          c:\program files (x86)\microsoft visual studio 12.0\vc\include\functional(385) : see reference to function template instantiation 'void std::_Func_class<_Ret,>::_Reset_alloc<_Ty,std::allocator<std::_Func_class<_Ret,>>>(_Fty &&,_Alloc)' being compiled
1>          with
1>          [
1>              _Ret=void
1>  ,            _Ty=int
1>  ,            _Fty=int
1>  ,            _Alloc=std::allocator<std::_Func_class<void,>>
1>          ]
1>          c:\program files (x86)\microsoft visual studio 12.0\vc\include\functional(385) : see reference to function template instantiation 'void std::_Func_class<_Ret,>::_Reset_alloc<_Ty,std::allocator<std::_Func_class<_Ret,>>>(_Fty &&,_Alloc)' being compiled
1>          with
1>          [
1>              _Ret=void
1>  ,            _Ty=int
1>  ,            _Fty=int
1>  ,            _Alloc=std::allocator<std::_Func_class<void,>>
1>          ]
1>          c:\program files (x86)\microsoft visual studio 12.0\vc\include\functional(671) : see reference to function template instantiation 'void std::_Func_class<_Ret,>::_Reset<_Ty>(_Fty &&)' being compiled
1>          with
1>          [
1>              _Ret=void
1>  ,            _Ty=int
1>  ,            _Fty=int
1>          ]
1>          c:\program files (x86)\microsoft visual studio 12.0\vc\include\functional(671) : see reference to function template instantiation 'void std::_Func_class<_Ret,>::_Reset<_Ty>(_Fty &&)' being compiled
1>          with
1>          [
1>              _Ret=void
1>  ,            _Ty=int
1>  ,            _Fty=int
1>          ]
1>          f:\work\teststdfunction\teststdfunction.cpp(16) : see reference to function template instantiation 'std::function<void (void)>::function<int>(_Fx &&)' being compiled
1>          with
1>          [
1>              _Fx=int
1>          ]
1>          f:\work\teststdfunction\teststdfunction.cpp(16) : see reference to function template instantiation 'std::function<void (void)>::function<int>(_Fx &&)' being compiled
1>          with
1>          [
1>              _Fx=int
1>          ]

(注意最后两个报告的错误中的“_Fx=int”)。

我可以忍受,因为将 MyStruct rc(NULL) 更改为 MyStruct rc(nullptr) 可以解决错误。然而,有两件事仍然是个谜:

1) 从 MyStruct ctor 中删除 const 限定符(MyStruct( std::function&lt;void()&gt;&amp; func) 给出了一个非常非常不同的错误:

1>f:\work\main\dev\common\teststdfunction\teststdfunction\teststdfunction.cpp(16): 错误 C2664:“MyStruct::MyStruct(const MyStruct &)”:无法转换 参数 1 从 'int' 到 'std::function &'

这比原来的错误更有意义,现在将 NULL 修复为 nullptr 并没有解决它。为什么 int(或 nullptr)拒绝转换为 std::function&lt;&gt;&amp; 但同意转换为 const std::function&lt;&gt;&amp;

2) 原始代码在 VS2010 中按预期编译和工作。它是一个不起眼的 VS2010 库错误吗?


编辑: 就 const/non const 问题而言,我现在认为涉及的强制转换和潜在的类型不匹配可能是一个红鲱鱼。传递的参数 - NULL 或 nullptr - 是文字,因此是 const。它只是不能绑定到非 const 引用。例如:

const int& a = 8;   // Ok
int& b = 9;    // error C2440: 'initializing' : cannot convert from 'int' to 'int &' 

听起来对吗?我还缺少什么吗?

【问题讨论】:

  • nullptrnullptr_t 类型的值,这是 std::function 的构造函数所接受的。 NULL 不一定定义为 nullptr 并且可能是不同的值。使用nullptr 而不是`NULL。
  • 如前所述,我确实更改为 nullptr。我不是在问为什么原始代码没有编译。
  • 文字根本不是 CONST。它们是右值。这是完全不同的事情。不要传播那个不正确的神话。
  • 为什么要还原我的编辑?修改后的代码也可以在 VS 中运行,并且实际上是符合标准的。
  • @pmr - 你在问我吗?我没有恢复任何编辑,但我确实添加了我自己的编辑,这可能会意外超出您的编辑(如果我们同时进行的话)。我很抱歉。

标签: c++ visual-c++-2010 visual-c++-2013


【解决方案1】:

这里的特殊之处在于构造函数模板约束和其他隐式转换。

nullptr 起作用的原因是因为std::function 有一个特定的构造函数接受它。此构造函数将始终是该参数的最佳匹配,因为函数模板的优先级较低,其他条件相同。

通常,0 会隐式转换为 nullptr,这很好。问题是它可以传递给从函数对象构造的不受约束的函数模板构造函数。这不需要隐式转换,因此所有都不相等,因此首选此构造函数 - 这会导致您看到 int 不是有效的函数对象的错误。

libstdc++ 和 libc++ 没有表现出这种行为,因为它们已经针对这个问题实现了 C++14 修复,这限制了构造函数。 C++11 没有,所以这种行为非常符合 C++11 实现。

这种问题就是为什么NULL 是一个你不应该使用的可怕东西。事实上,VS2010 团队不得不在最后一刻将nullptr 作为附加功能赶出大门,因为NULL 与 C++ 中的所有其他功能交互非常糟糕,尤其是与 C++11 交互。

对于const 与非const 参考,其他答案已充分解释了该问题。

在使用 std::function 而没有 C++14 中提供的受限构造函数修复时,您可以找到其他 WTF——这不是唯一的。总而言之,它是 C++11 标准中的缺陷,而 not 在 VS 中。 VS2010编译它可能是编译器重载解析错误。

【讨论】:

  • 感谢您的回答。我仍然不明白的是为什么 SFINAE 没有启动。说 0 确实匹配了不受约束的模板,但之后却失败了——接下来不应该尝试 nullptr_t 特化来匹配吗?
  • @Ofek:SFINAE 仅适用于函数签名。它一般不适用于身体。
【解决方案2】:

T 转换为U&amp; 永远不会起作用,这是故意的。您可以转换为U const&amp;。原因是临时 U 对象的更改不会传播回 T 值。

VS2010 在这方面有点问题,并且确实允许强制转换(但使用适当的设置,会发出警告)

【讨论】:

  • 不确定我是否理解。显然 void f(int& i) 可以接受一个 int - 这不符合从 T 转换为 U& 的条件吗?
  • @OfekShilon:不,它不能:f(4) 被拒绝,尽管4 是一个明显的int。这只是 TT&amp; - 你也不能更改 4
  • 不是 4 一个 const int 吗? AFAIK 无法将 4 绑定到 int& 是双重的:(a)它是 const,(b)它是一个右值。
  • 4 是 const int 这就是为什么它不能是 int&。如前所述,它是 MSVC 扩展。 blogs.msdn.microsoft.com/vcblog/2016/11/16/permissive-switch(请参阅:将非常量引用绑定到临时对象)
  • @NN_:听起来很合理,不,4 不是const int。这是一个标准的prvalue。并且非类非数组纯右值不能是 cv 限定的。见en.cppreference.com/w/cpp/language/value_category
猜你喜欢
  • 2021-01-04
  • 2018-03-15
  • 2012-06-25
  • 1970-01-01
  • 2014-12-11
  • 2017-07-08
  • 2018-11-06
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多