MSVC++14 上的 C++11 中的不可能的快速委托

Posted

技术标签:

【中文标题】MSVC++14 上的 C++11 中的不可能的快速委托【英文标题】:Impossibly fast Delegate in C++11 on MSVC++14 【发布时间】:2016-03-10 09:29:41 【问题描述】:

问题

这个问题与这里关于函数指针的几个问题有关(我不会列出它们),但最重要的是它与here 发布的代码有关。该帖子声称代码应该符合标准,并且可以在 Apple Clang 上编译。

在VS2015,即MSVC++14,我还没有编译成功

此错误的根源可能是什么?为什么不是 Clang?


第一个错误

第一个错误重复多次:

...\Delegate.hpp(329): error C2514: 'Delegate<R(A...)>::is_member_pair<<unnamed-symbol>>':
class has no constructors

is_member_pair_const 也是如此。涉及到以下代码部分:

  template <typename T>
  static typename ::std::enable_if<
    !(is_member_pair<T> ||
    is_const_member_pair<T>),
    R>::type
  functor_stub(void* const object_ptr, A&&... args)
  
    return (*static_cast<T*>(object_ptr))(::std::forward<A>(args)...);
  

第二个错误

出现多个错误,但第一个通常是最重要的:

...\Delegate.hpp(340): error C2059: syntax error: '<end Parse>'
...\Delegate.hpp(349): note: see reference to class template instantiation 'Delegate<R(A...)>' being compiled

然后跟随一些语法错误,无法识别的模板声明/定义等。有问题的代码:

  template <typename T>
  static typename ::std::enable_if<
    is_member_pair<T> ||
    is_const_member_pair<T>,
    R
  >::type
  functor_stub(void* const object_ptr, A&&... args)
  
    return (static_cast<T*>(object_ptr)->first->*
      static_cast<T*>(object_ptr)->second)(::std::forward<A>(args)...);
  

为了完整性:整个 Delegate.hpp:

Code Source 和 Base Article

#pragma once
#ifndef DELEGATE_HPP
#define DELEGATE_HPP

#include <cassert>

#include <memory>

#include <new>

#include <type_traits>

#include <utility>

template <typename T> class Delegate;

template<class R, class ...A>
class Delegate<R (A...)>

  using stub_ptr_type = R (*)(void*, A&&...);

  Delegate(void* const o, stub_ptr_type const m) noexcept :
    object_ptr_(o),
    stub_ptr_(m)
  
  

public:
  Delegate() = default;

  Delegate(Delegate const&) = default;

  Delegate(Delegate&&) = default;

  Delegate(::std::nullptr_t const) noexcept : Delegate()  

  template <class C, typename =
    typename ::std::enable_if< ::std::is_class<C>>::type>
  explicit Delegate(C const* const o) noexcept :
    object_ptr_(const_cast<C*>(o))
  
  

  template <class C, typename =
    typename ::std::enable_if< ::std::is_class<C>>::type>
  explicit Delegate(C const& o) noexcept :
    object_ptr_(const_cast<C*>(&o))
  
  

  template <class C>
  Delegate(C* const object_ptr, R (C::* const method_ptr)(A...))
  
    *this = from(object_ptr, method_ptr);
  

  template <class C>
  Delegate(C* const object_ptr, R (C::* const method_ptr)(A...) const)
  
    *this = from(object_ptr, method_ptr);
  

  template <class C>
  Delegate(C& object, R (C::* const method_ptr)(A...))
  
    *this = from(object, method_ptr);
  

  template <class C>
  Delegate(C const& object, R (C::* const method_ptr)(A...) const)
  
    *this = from(object, method_ptr);
  

  template <
    typename T,
    typename = typename ::std::enable_if<
      !::std::is_same<Delegate, typename ::std::decay<T>::type>
    >::type
  >
  Delegate(T&& f) :
    store_(operator new(sizeof(typename ::std::decay<T>::type)),
      functor_deleter<typename ::std::decay<T>::type>),
    store_size_(sizeof(typename ::std::decay<T>::type))
  
    using functor_type = typename ::std::decay<T>::type;

    new (store_.get()) functor_type(::std::forward<T>(f));

    object_ptr_ = store_.get();

    stub_ptr_ = functor_stub<functor_type>;

    deleter_ = deleter_stub<functor_type>;
  

  Delegate& operator=(Delegate const&) = default;

  Delegate& operator=(Delegate&&) = default;

  template <class C>
  Delegate& operator=(R (C::* const rhs)(A...))
  
    return *this = from(static_cast<C*>(object_ptr_), rhs);
  

  template <class C>
  Delegate& operator=(R (C::* const rhs)(A...) const)
  
    return *this = from(static_cast<C const*>(object_ptr_), rhs);
  

  template <
    typename T,
    typename = typename ::std::enable_if<
      !::std::is_same<Delegate, typename ::std::decay<T>::type>
    >::type
  >
  Delegate& operator=(T&& f)
  
    using functor_type = typename ::std::decay<T>::type;

    if ((sizeof(functor_type) > store_size_) || !store_.unique())
    
      store_.reset(operator new(sizeof(functor_type)),
        functor_deleter<functor_type>);

      store_size_ = sizeof(functor_type);
    
    else
    
      deleter_(store_.get());
    

    new (store_.get()) functor_type(::std::forward<T>(f));

    object_ptr_ = store_.get();

    stub_ptr_ = functor_stub<functor_type>;

    deleter_ = deleter_stub<functor_type>;

    return *this;
  

  template <R (* const function_ptr)(A...)>
  static Delegate from() noexcept
  
    return  nullptr, function_stub<function_ptr> ;
  

  template <class C, R (C::* const method_ptr)(A...)>
  static Delegate from(C* const object_ptr) noexcept
  
    return  object_ptr, method_stub<C, method_ptr> ;
  

  template <class C, R (C::* const method_ptr)(A...) const>
  static Delegate from(C const* const object_ptr) noexcept
  
    return  const_cast<C*>(object_ptr), const_method_stub<C, method_ptr> ;
  

  template <class C, R (C::* const method_ptr)(A...)>
  static Delegate from(C& object) noexcept
  
    return  &object, method_stub<C, method_ptr> ;
  

  template <class C, R (C::* const method_ptr)(A...) const>
  static Delegate from(C const& object) noexcept
  
    return  const_cast<C*>(&object), const_method_stub<C, method_ptr> ;
  

  template <typename T>
  static Delegate from(T&& f)
  
    return ::std::forward<T>(f);
  

  static Delegate from(R (* const function_ptr)(A...))
  
    return function_ptr;
  

  template <class C>
  using member_pair =
    ::std::pair<C* const, R (C::* const)(A...)>;

  template <class C>
  using const_member_pair =
    ::std::pair<C const* const, R (C::* const)(A...) const>;

  template <class C>
  static Delegate from(C* const object_ptr,
    R (C::* const method_ptr)(A...))
  
    return member_pair<C>(object_ptr, method_ptr);
  

  template <class C>
  static Delegate from(C const* const object_ptr,
    R (C::* const method_ptr)(A...) const)
  
    return const_member_pair<C>(object_ptr, method_ptr);
  

  template <class C>
  static Delegate from(C& object, R (C::* const method_ptr)(A...))
  
    return member_pair<C>(&object, method_ptr);
  

  template <class C>
  static Delegate from(C const& object,
    R (C::* const method_ptr)(A...) const)
  
    return const_member_pair<C>(&object, method_ptr);
  

  void reset()  stub_ptr_ = nullptr; store_.reset(); 

  void reset_stub() noexcept  stub_ptr_ = nullptr; 

  void swap(Delegate& other) noexcept  ::std::swap(*this, other); 

  bool operator==(Delegate const& rhs) const noexcept
  
    return (object_ptr_ == rhs.object_ptr_) && (stub_ptr_ == rhs.stub_ptr_);
  

  bool operator!=(Delegate const& rhs) const noexcept
  
    return !operator==(rhs);
  

  bool operator<(Delegate const& rhs) const noexcept
  
    return (object_ptr_ < rhs.object_ptr_) ||
      ((object_ptr_ == rhs.object_ptr_) && (stub_ptr_ < rhs.stub_ptr_));
  

  bool operator==(::std::nullptr_t const) const noexcept
  
    return !stub_ptr_;
  

  bool operator!=(::std::nullptr_t const) const noexcept
  
    return stub_ptr_;
  

  explicit operator bool() const noexcept  return stub_ptr_; 

  R operator()(A... args) const
  
//  assert(stub_ptr);
    return stub_ptr_(object_ptr_, ::std::forward<A>(args)...);
  

private:
  friend struct ::std::hash<Delegate>;

  using deleter_type = void (*)(void*);

  void* object_ptr_;
  stub_ptr_type stub_ptr_;

  deleter_type deleter_;

  ::std::shared_ptr<void> store_;
  ::std::size_t store_size_;

  template <class T>
  static void functor_deleter(void* const p)
  
    static_cast<T*>(p)->~T();

    operator delete(p);
  

  template <class T>
  static void deleter_stub(void* const p)
  
    static_cast<T*>(p)->~T();
  

  template <R (*function_ptr)(A...)>
  static R function_stub(void* const, A&&... args)
  
    return function_ptr(::std::forward<A>(args)...);
  

  template <class C, R (C::*method_ptr)(A...)>
  static R method_stub(void* const object_ptr, A&&... args)
  
    return (static_cast<C*>(object_ptr)->*method_ptr)(
      ::std::forward<A>(args)...);
  

  template <class C, R (C::*method_ptr)(A...) const>
  static R const_method_stub(void* const object_ptr, A&&... args)
  
    return (static_cast<C const*>(object_ptr)->*method_ptr)(
      ::std::forward<A>(args)...);
  

  template <typename>
  struct is_member_pair : std::false_type  ;

  template <class C>
  struct is_member_pair< ::std::pair<C* const,
    R (C::* const)(A...)> > : std::true_type
  
  ;

  template <typename>
  struct is_const_member_pair : std::false_type  ;

  template <class C>
  struct is_const_member_pair< ::std::pair<C const* const,
    R (C::* const)(A...) const> > : std::true_type
  
  ;

  template <typename T>
  static typename ::std::enable_if<
    !(is_member_pair<T> ||
    is_const_member_pair<T>),
    R
  >::type
  functor_stub(void* const object_ptr, A&&... args)
  
    return (*static_cast<T*>(object_ptr))(::std::forward<A>(args)...);
  

  template <typename T>
  static typename ::std::enable_if<
    is_member_pair<T> ||
    is_const_member_pair<T>,
    R
  >::type
  functor_stub(void* const object_ptr, A&&... args)
  
    return (static_cast<T*>(object_ptr)->first->*
      static_cast<T*>(object_ptr)->second)(::std::forward<A>(args)...);
  
;

namespace std

  template <typename R, typename ...A>
  struct hash<::Delegate<R (A...)> >
  
    size_t operator()(::Delegate<R (A...)> const& d) const noexcept
    
      auto const seed(hash<void*>()(d.object_ptr_));

      return hash<typename ::Delegate<R (A...)>::stub_ptr_type>()(
        d.stub_ptr_) + 0x9e3779b9 + (seed << 6) + (seed >> 2);
    
  ;


#endif // DELEGATE_HPP

【问题讨论】:

[OT]:using stub_ptr_type = R (*)(void*, A&amp;&amp;...); 似乎很可疑。你真的想要右值引用吗? (实际上适用于大多数A&amp;&amp;)。 @Jarod42 以什么方式可疑?我认为使用它们是为了让它快速运行。 “右值引用允许函数在编译时分支”,见thbecker.net/articles/rvalue_references/section_03.html 但我还没有真正掌握这个实现。很高级,不是我写的。 尝试将is_member_pair&lt;T&gt; 替换为is_member_pair&lt;T&gt;::valueis_const_member_pair 也一样) @IgorTandetnik 似乎已经成功了,谢谢!如果您将其发布为带有简短描述的答案,我会检查它。 【参考方案1】:

正如 Igor 评论的那样,以下解决了问题:将 is_member_pair&lt;T&gt; 替换为 is_member_pair&lt;T&gt;::value 并为 is_const_member_pair 做同样的事情。

【讨论】:

它适用于我 (Visual Studio 2017),但我不知道为什么...在哪里可以找到一些关于此的文档以便我研究它?

以上是关于MSVC++14 上的 C++11 中的不可能的快速委托的主要内容,如果未能解决你的问题,请参考以下文章

GCC C++14/17 成员函数指针模板参数的区别

我可以在 Windows7x64 (MSVC) 和 Linux64 (GCC4.8.2) 的 .cu 文件 (CUDA5.5) 中使用 C++11 吗?

QtCreator集成的MSVC套件有问题

错误:命令 'C:\\Visual Studio\\2017\\Community\\VC\\Tools\\MSVC\\14.14.26428\\bin\\HostX86\\x64\\cl.exe'

如何从现有对象的不同类调用方法(Obj C)

gcc C++ 14 与 msvc++ 2015 中的级联宏