【问题标题】:Variadic Template conversion to std::function<R(ARGS...)> works with GCC and not MSVC2013, why?到 std::function<R(ARGS...)> 的可变参数模板转换适用于 GCC 而不是 MSVC2013,为什么?
【发布时间】:2015-09-15 06:46:32
【问题描述】:

如果这是重复的,我很抱歉。但我在搜索中找不到任何内容。

我可以使用 c++11/c++14 的任何最新功能。如有必要,我可以升级到 VS2015。

我正在尝试编写一个类,该类在分配时将自动转换为具有特定签名的 std::function。我有适用于 GCC 的代码,但在 MSVC2013 上失败了。该代码是一个重新创建错误的 sn-p。 WTF MSVC?!

我也知道这是有风险的代码,自动转换函数指针等,但它是用于插件库的私有实现,我只想定义一次函数签名。

如果有另一种方法来编写代码,在 main() 中完成相同的功能并同时在两者上工作,我会全力以赴。

GCC c++11 工作正常 - Demo

#include <functional>
#include <string>
#include <iostream>

class FunctionPointer
{
    void* fp;
public:
    FunctionPointer(void* ptr)
        : fp(ptr)
    {}

    // Overload casting operator to 
    // a certain function signiture
    template<class R, class... ARGS>
    operator std::function<R(ARGS...)>(){
        typedef R(*func_ptr)(ARGS...);
        return std::function<R(ARGS...)>((func_ptr)fp);
    }
};

void hello(std::string msg){
    std::cout << "Hello " << msg << std::endl;
}

int main() {

    FunctionPointer f((void*)hello);

    std::function<void(std::string)> func_hello = f;

    func_hello("World!");

    return 0;
}

当我将行更改为此时,MSVC 工作......

std::function<void(std::string)> func_hello = f.operator std::function<void(std::string)>();

当我遇到这个时,MSVC 失败并出现同样的错误...

std::function<void(std::string)> func_hello = (std::function<void(std::string)>)f;

MSVC 在一个文件中出现以下错误,至少可以说是难以阅读。似乎是在推断错误的函数签名。

xrefwrap.h:283 - error C2064: term does not evaluate to a function taking 1 arguments


1>c:\program files (x86)\microsoft visual studio 12.0\vc\include\xrefwrap(283): error C2064: term does not evaluate to a function taking 1 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<FunctionPointer,false>::_ApplyX<_Rx,_Ty>(_Ty &&)' being compiled
1>          with
1>          [
1>              _Ret=void
1>  ,            _Rx=void
1>  ,            _Ty=std::basic_string<char,std::char_traits<char>,std::allocator<char>>
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<FunctionPointer,false>::_ApplyX<_Rx,_Ty>(_Ty &&)' being compiled
1>          with
1>          [
1>              _Ret=void
1>  ,            _Rx=void
1>  ,            _Ty=std::basic_string<char,std::char_traits<char>,std::allocator<char>>
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,std::string>::_Do_call(std::string &&)'
1>          with
1>          [
1>              _Alloc=std::allocator<std::_Func_class<void,std::string>>
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,std::string>' being compiled
1>          with
1>          [
1>              _Alloc=std::allocator<std::_Func_class<void,std::string>>
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,std::string>::_Do_alloc<_Myimpl,FunctionPointer&,_Alloc>(_Fty,_Alloc)' being compiled
1>          with
1>          [
1>              _Ret=void
1>  ,            _Alloc=std::allocator<std::_Func_class<void,std::string>>
1>  ,            _Fty=FunctionPointer &
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,std::string>::_Do_alloc<_Myimpl,FunctionPointer&,_Alloc>(_Fty,_Alloc)' being compiled
1>          with
1>          [
1>              _Ret=void
1>  ,            _Alloc=std::allocator<std::_Func_class<void,std::string>>
1>  ,            _Fty=FunctionPointer &
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,std::string>::_Reset_alloc<FunctionPointer&,std::allocator<std::_Func_class<_Ret,std::string>>>(_Fty,_Alloc)' being compiled
1>          with
1>          [
1>              _Ret=void
1>  ,            _Fty=FunctionPointer &
1>  ,            _Alloc=std::allocator<std::_Func_class<void,std::string>>
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,std::string>::_Reset_alloc<FunctionPointer&,std::allocator<std::_Func_class<_Ret,std::string>>>(_Fty,_Alloc)' being compiled
1>          with
1>          [
1>              _Ret=void
1>  ,            _Fty=FunctionPointer &
1>  ,            _Alloc=std::allocator<std::_Func_class<void,std::string>>
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,std::string>::_Reset<FunctionPointer&>(_Fty)' being compiled
1>          with
1>          [
1>              _Ret=void
1>  ,            _Fty=FunctionPointer &
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,std::string>::_Reset<FunctionPointer&>(_Fty)' being compiled
1>          with
1>          [
1>              _Ret=void
1>  ,            _Fty=FunctionPointer &
1>          ]
1>          c:\users\cameron\desktop\desktop\programming\projects\c++ projects\garbage\templatetest\main.cpp(32) : see reference to function template instantiation 'std::function<void (std::string)>::function<FunctionPointer&>(_Fx)' being compiled
1>          with
1>          [
1>              _Fx=FunctionPointer &
1>          ]
1>          c:\users\cameron\desktop\desktop\programming\projects\c++ projects\garbage\templatetest\main.cpp(32) : see reference to function template instantiation 'std::function<void (std::string)>::function<FunctionPointer&>(_Fx)' being compiled
1>          with
1>          [
1>              _Fx=FunctionPointer &
1>          ]

【问题讨论】:

  • 其中至少有一部分是“std::function 有一个构造函数,它可以接受阳光下的一切”问题。这是标准中已修复的缺陷,但修复需要表达式 SFINAE 来实现,而 MSVC 尚不支持。
  • 简单。只需去掉FunctionPointer,直接从hello构造func_hello。在您的示例 main 中没有理由通过 void* 往返。您的实际代码库中可能有原因,但我没有看到任何证据,我对此表示怀疑。
  • @Yakk:但是 GetProcAddress()/dlsym() 兄弟。
  • 这将违背所有明智的建议,但是由于您说这仅供内部使用,并且您应该确切地知道自己在做什么,所以这里是:my::function 模板派生自std::function,构造函数采用void*,它与您的转换函数基本相同。这意味着您摆脱了FunctionPointer 并直接使用void*。您仅将其用于初始化;当你传递它时,你复制/绑定到std::function的引用。
  • 抱歉,我只是想围绕您要完成的事情来思考一下,以便我可以提出其他建议。 FunctionPointer 直接使用 std::function 给你带来了什么?类型擦除?

标签: c++ c++11 variadic-templates c++14


【解决方案1】:

这是解决您的问题的另一种方法。如果我对 MSVC 2015 功能的理解是正确的,它应该在那里工作。

(我假设您的问题是希望相对透明地将 void* 转换为未知函数到 std::function 与未知函数实际具有的签名,而不必重复函数的签名。)

我不是在我们被强制转换为std::function 的点上强制转换 void 指针,而是在调用函数并计算返回值(或者,当没有计算返回值时)通过一个“返回类型扣除”技巧:

template<class...Args>
struct void_ptr_deferred_execution_t {
  std::tuple<Args&&...> args;
  void const* pf = nullptr;
  void_ptr_deferred_execution_t( std::tuple<Args&&...> a, void const* p ):
    args(std::move(a)),
    pf(p)
  {}
  template<class R, size_t...Is>
  R invoke( std::index_sequence<Is...> ){
    using f_t = R(*)(Args...);
    f_t f = f_t(pf);
    pf = nullptr;
    return f(std::forward<Args>(std::get<Is>(args))...);
  }
  template<class R>
  operator R()&&{
    return invoke<R>( std::index_sequence_for<Args...>{} );
  }
  ~void_ptr_deferred_execution_t() {
    if (pf) invoke<void>(std::index_sequence_for<Args...>{});
  }
};

class FunctionPointer
{
  void* fp;
public:
  FunctionPointer(void* ptr)
    : fp(ptr)
  {}

  template<class...Args>
  void_ptr_deferred_execution_t<Args...>
  operator()(Args&&...args)const {
    return { std::forward_as_tuple(std::forward<Args>(args)...), fp };
  }
};

live example.

std::function 调用它们的可调用对象时,它们要么丢弃结果,要么将其转换为R。从传递给可调用对象的参数,加上返回值转换为的类型,我可以(大部分)重构调用我的std::function 的签名。

此时,我将void* 转换为指向该函数的指针,调用它并返回结果。

如果我从未被强制转换为某个东西,那么在破坏点我将我的函数指针转换为一个 void 返回函数,调用它,然后就完成了。


注意事项:

请注意,在您的示例中直接调用 FunctionPointer 与将其传递给 std::function 一样危险(非常)。

真的,将函数指针存储在 std::function 中是多余的。

未在 MSVC2015 中测试,但我没有看到任何在 MSVC2015 中不起作用的东西。

如果您的函数返回voidstd::function 的某些实现可能无法使用上述内容。然而,这将是一个编译时错误。

上面还假设没有带有右值引用参数的函数,因为在调用时,我无法区分是从std::function&lt;void(T)&gt;std::function&lt;void(T&amp;&amp;)&gt; 调用的。我认为在这种情况下它是void(T)

【讨论】:

    【解决方案2】:

    这就是我避免在课堂外进行一些强制转换的方法:

    class FunctionPointer
    {
     void* fp;
        public:
    FunctionPointer() = default;
    
    template<class Ret, class Fn, class... Args> void assign_fn(Fn fn, Args... args)
    {
        std::function<Ret(Args...)> *f = new std::function<Ret(Args...)>;
        *f = Fn; // Tip: you can use enable_if combined with is_assignable to make sure this can be done.
        // Both are regular pointers, so conversion shouldn't be a problem.
        fp = reinterpret_cast<void*>(f);
    }
    
    // Overload casting operator to 
    // a certain function signature
    template<class R, class... ARGS>
    operator std::function<R(ARGS...)>(){
        typedef R(*func_ptr)(ARGS...);
        return std::function<R(ARGS...)>((func_ptr)fp);
    }
    };
    

    此外,您可以利用 std::function 可以指向任何可调用对象这一事实,通过使您的函数指针可调用(添加一个 operator()),这将非常相似转换为 std::function。

    template<class Ret, class... Args> Ret operator()(Args... args)
    {
         std::function<Ret(Args...)> f = *(reinterpret_cast<std::function<Ret(Args...)>*>(fp));
         return f(args...);
    }
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2012-11-19
      • 2012-11-11
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多