std :: function赋值应该忽略返回类型吗?

时间:2014-08-19 14:21:57

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

根据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其他堆栈溢出问题表明它应该有用。

2 个答案:

答案 0 :(得分:9)

代码在C ++ 11中是未定义的行为,在C ++ 14中是不正确的。 C ++ 14将此备注添加到此构造函数的规范中:

  

备注:这些构造函数不应参与重载   解析除非f是可调用的(20.9.11.2)参数类型   ArgTypes...并返回R类型。

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

中定义
  

f类型的可调用对象F对于参数类型ArgTypes Callable   如果表达式 INVOKE R,则返回类型(f, declval<ArgTypes>()..., R),被视为未评估的操作数   (第5条),形成良好(20.9.2)。

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

在C ++ 11中,这些语句属于 Requries 子句,这意味着由客户端决定是否正确,如果客户端失败,任何事情都可能发生,包括成功编译。

LWG 2132改变了这一点。

答案 1 :(得分:3)

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中精确地移动到lambda中 - 您可以通过添加更多样板来消除它。)

尚未运行。