【问题标题】:Should std::function assignment ignore return type? [duplicate]std::function 赋值是否应该忽略返回类型? [复制]
【发布时间】:2014-08-19 14:21:57
【问题描述】:

根据 C++11 或 C++14 标准,下面的代码是有效的 C++ 吗?

#include <functional>

int ReturnInt()
{
  return 5;
}

int main( int argc, char **argv )
{
  std::function< void () > BoundType = ReturnInt;
  return 0;
}

该代码可以使用最新的 cygwin 版本的 gcc (4.8.3) 和 clang (4.3.2) 编译,但不能使用 Visual Studio 2013、Visual Studio 2013 年 11 月 CTP 或 Visual Studio 14 预览版。如果 std::function 更改为 boost::function,它也可以在所有平台上编译。

我发现 this 其他堆栈溢出问题表明它应该可以工作。

【问题讨论】:

  • 是的,看起来是同一个问题。我确实在发布之前尝试过搜索它,但显然不够难,抱歉。

标签: c++ c++11 std-function c++14


【解决方案1】:

代码在 C++11 中是未定义的行为,在 C++14 中格式错误。 C++14 将此 Remark 添加到此构造函数的规范中:

备注:这些构造函数不得参与重载 分辨率除非 f 对于参数类型是 Callable (20.9.11.2) ArgTypes... 和返回类型 R

Callable 定义在 [func.wrap.func]/p2:

F 类型的可调用对象 f 对于参数类型 ArgTypes可调用 如果表达式 INVOKE (f, declval&lt;ArgTypes&gt;()..., R),则返回类型 R,被视为未计算的操作数 (第 5 条),格式正确 (20.9.2)。

为了使这个 INVOKE 格式正确,没有RINVOKE 的返回类型必须隐式转换为R([func.require]/ p2)。

在 C++11 中,这些语句位于 Requries 子句下,这意味着由客户端来确保它们正确,如果客户端失败,任何事情都可能发生,包括成功编译。

这已被 LWG 2132 更改。

【讨论】:

【解决方案2】:

C++11 和 14 中的std::function 没有您想要的行为。

SFINAE 也无法检测到严重的过载。

我们可以将它包装成另一种类型,既具有您想要的行为(void 丢弃返回),又具有 SFINAE 错误过载检测,而我们如下:

template<class Sig>
struct checked_function;

template<class R, class... Args>
struct checked_function<R(Args...)>:std::function<R(Args...)> {
  using function = std::function<R(Args...)>;
  checked_function(std::nullptr_t):function() {}
  checked_function():function() {}
  template<class F, class=typename std::enable_if<
    std::is_convertible<
      typename std::result_of< F(Args...) >::type
      , R
    >::value
  >::type>
  checked_function( F&& f ):function( std::forward<F>(f) ) {}

  template<class F, class=typename std::enable_if<
    std::is_convertible<
      typename std::result_of< F(Args...) >::type
      , R
    >::value
  >::type>
  checked_function& operator=( F&& f ) { return function::operator=( std::forward<F>(f) ); }

  checked_function& operator=( checked_function const& o ) = default;
  checked_function& operator=( checked_function && o ) = default;
  checked_function( checked_function const& o ) = default;
  checked_function( checked_function && o ) = default;
};

template<class... Args>
struct checked_function<void(Args...)>:std::function<void(Args...)> {
  using function = std::function<void(Args...)>;
  checked_function(std::nullptr_t):function() {}
  checked_function():function() {}
  template<class F, class=typename std::enable_if<
    std::is_same<
      typename std::result_of< F(Args...) >::type
      , void
    >::value
  >::type>
  checked_function( F&& f, int*unused=nullptr ):function( std::forward<F>(f) ) {}

  template<class F>
  static auto wrap(F&& f){
    return [f_=std::forward<F>(f)](auto&&...args){
      f_( std::forward<decltype(args)>(args)... );
    };
  }
  template<class F, class=typename std::enable_if<
    !std::is_same<
      typename std::result_of< F(Args...) >::type
      , void
    >::value
  >::type>
  checked_function( F&& f, void*unused=nullptr ):
    function( wrap(std::forward<F>(f)) ) {}

   template<class F>
  typename std::enable_if<
    !std::is_same<
      typename std::result_of< F(Args...) >::type
      , void
    >::value,
    checked_function&
  >::type operator=( F&& f ) { return function::operator=( wrap(std::forward<F>(f)) ); }

  template<class F>
  typename std::enable_if<
    std::is_same<
      typename std::result_of< F(Args...) >::type
      , void
    >::value,
    checked_function&
  >::type operator=( F&& f ) { return function::operator=( std::forward<F>(f) ); }

  checked_function& operator=( checked_function const& o ) = default;
  checked_function& operator=( checked_function && o ) = default;
  checked_function( checked_function const& o ) = default;
  checked_function( checked_function && o ) = default;
};

它现在在 C++14 中编译(不是在 C++11 中,因为 wrapwrap 可以在调用点替换为它自己的主体副本,所以......)。可能会减少一堆样板。

它使用了一些 C++14 特性(确切地说是 wrap 中的 move-into-lambda -- 你可以通过添加更多样板来消除它)。

尚未运行。

【讨论】:

  • 您希望它像checked_function&lt; void() &gt; BoundType = &amp;ReturnInt; 这样工作吗?我修正了一些错别字和一些东西,但它仍然无法编译pastebin.com/VdPqZCzx。我认为 void 专业化中的 std::is_same 是错误的,您还需要编写实际上忽略返回类型的 operator() 重载。
  • @fun4jimmy 你把wraps非void返回函数的构造函数删掉了,自然就不能wrap他们了。我用一个可以编译的帖子编辑了这篇文章。
  • 啊抱歉,我以为你错过了复制和粘贴部分,我的错。这行得通,太棒了,谢谢。
猜你喜欢
  • 1970-01-01
  • 2015-06-06
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2020-11-26
相关资源
最近更新 更多