C++ 可变版本的FastDelegate和附加值复制
我正在使用可变模板移植到C++0xC++ 可变版本的FastDelegate和附加值复制,c++,c++11,rvalue-reference,variadic-templates,C++,C++11,Rvalue Reference,Variadic Templates,我正在使用可变模板移植到C++0x #include "FastDelegate.h" template<class R=fastdelegate::detail::DefaultVoid, class ...P> class fast_delegate_base { private: typedef typename fastdelegate::detail::DefaultVoidToVoid<R>::type des
#include "FastDelegate.h"
template<class R=fastdelegate::detail::DefaultVoid, class ...P>
class fast_delegate_base {
private:
typedef typename fastdelegate::detail::DefaultVoidToVoid<R>::type desired_ret_t;
typedef desired_ret_t (*static_func_ptr)(P...);
typedef R (*unvoid_static_func_ptr)(P...);
typedef R (fastdelegate::detail::GenericClass::*generic_mem_fn)(P...);
typedef fastdelegate::detail::ClosurePtr<generic_mem_fn, static_func_ptr, unvoid_static_func_ptr> closure_t;
closure_t closure_;
public:
// Typedefs to aid generic programming
typedef fast_delegate_base type;
// Construction and comparison functions
fast_delegate_base() { clear(); }
fast_delegate_base(const fast_delegate_base &x)
{
closure_.CopyFrom(this, x.closure_);
}
void operator = (const fast_delegate_base &x)
{
closure_.CopyFrom(this, x.closure_);
}
bool operator ==(const fast_delegate_base &x) const
{
return closure_.IsEqual(x.closure_);
}
bool operator !=(const fast_delegate_base &x) const
{
return !closure_.IsEqual(x.closure_);
}
bool operator <(const fast_delegate_base &x) const
{
return closure_.IsLess(x.closure_);
}
bool operator >(const fast_delegate_base &x) const
{
return x.closure_.IsLess(closure_);
}
// Binding to non-const member functions
template<class X, class Y>
fast_delegate_base(Y *pthis, desired_ret_t (X::* function_to_bind)(P...) )
{
closure_.bindmemfunc(fastdelegate::detail::implicit_cast<X*>(pthis), function_to_bind);
}
template<class X, class Y>
inline void bind(Y *pthis, desired_ret_t (X::* function_to_bind)(P...))
{
closure_.bindmemfunc(fastdelegate::detail::implicit_cast<X*>(pthis), function_to_bind);
}
// Binding to const member functions.
template<class X, class Y>
fast_delegate_base(const Y *pthis, desired_ret_t (X::* function_to_bind)(P...) const)
{
closure_.bindconstmemfunc(fastdelegate::detail::implicit_cast<const X*>(pthis), function_to_bind);
}
template<class X, class Y>
inline void bind(const Y *pthis, desired_ret_t (X::* function_to_bind)(P...) const)
{
closure_.bindconstmemfunc(fastdelegate::detail::implicit_cast<const X *>(pthis), function_to_bind);
}
// Static functions. We convert them into a member function call.
// This constructor also provides implicit conversion
fast_delegate_base(desired_ret_t (*function_to_bind)(P...) )
{
bind(function_to_bind);
}
// for efficiency, prevent creation of a temporary
void operator = (desired_ret_t (*function_to_bind)(P...) )
{
bind(function_to_bind);
}
inline void bind(desired_ret_t (*function_to_bind)(P...))
{
closure_.bindstaticfunc(this, &fast_delegate_base::invoke_static_func, function_to_bind);
}
// Invoke the delegate
template<typename ...A>
R operator()(A&&... args) const
{
return (closure_.GetClosureThis()->*(closure_.GetClosureMemPtr()))(std::forward<A>(args)...);
}
// Implicit conversion to "bool" using the safe_bool idiom
private:
typedef struct safe_bool_struct
{
int a_data_pointer_to_this_is_0_on_buggy_compilers;
static_func_ptr m_nonzero;
} useless_typedef;
typedef static_func_ptr safe_bool_struct::*unspecified_bool_type;
public:
operator unspecified_bool_type() const { return empty()? 0: &safe_bool_struct::m_nonzero; }
// necessary to allow ==0 to work despite the safe_bool idiom
inline bool operator==(static_func_ptr funcptr) { return closure_.IsEqualToStaticFuncPtr(funcptr); }
inline bool operator!=(static_func_ptr funcptr) { return !closure_.IsEqualToStaticFuncPtr(funcptr); }
// Is it bound to anything?
inline bool operator ! () const { return !closure_; }
inline bool empty() const { return !closure_; }
void clear() { closure_.clear();}
// Conversion to and from the DelegateMemento storage class
const fastdelegate::DelegateMemento & GetMemento() { return closure_; }
void SetMemento(const fastdelegate::DelegateMemento &any) { closure_.CopyFrom(this, any); }
private:
// Invoker for static functions
R invoke_static_func(P... args) const
{
return (*(closure_.GetStaticFunction()))(args...);
}
};
// fast_delegate<> is similar to std::function, but it has comparison operators.
template<typename _Signature>
class fast_delegate;
template<typename R, typename ...P>
class fast_delegate<R(P...)> : public fast_delegate_base<R, P...>
{
public:
typedef fast_delegate_base<R, P...> BaseType;
fast_delegate() : BaseType() { }
template<class X, class Y>
fast_delegate(Y * pthis, R (X::* function_to_bind)(P...))
: BaseType(pthis, function_to_bind)
{ }
template<class X, class Y>
fast_delegate(const Y *pthis, R (X::* function_to_bind)(P...) const)
: BaseType(pthis, function_to_bind)
{ }
fast_delegate(R (*function_to_bind)(P...))
: BaseType(function_to_bind)
{ }
void operator = (const BaseType &x)
{
*static_cast<BaseType*>(this) = x;
}
};
该代码的结果是:
C1()
C1(const C1&)
C1(const C1&)
C1::test(1234)
~C1()
~C1()
~C1()
你有没有办法避免这个额外的值拷贝?在我看来,这个拷贝是类设计中固有的,特别是
invoke\u static\u func
的存在
从我所看到的,这是一个将静态函数和成员函数规范化为成员函数的代理,因此它们的每次分派都可以作为成员函数调用来完成。唯一的区别是成员是fast\u delegate\u base
实例,而不是目标函数所属的任何类的实例
因此,在调用静态函数时会有一个额外的调用帧,为了消除这个额外的副本,您需要使额外的调用帧(invoke\u static\u func
)通过引用获取其参数(如果参数类型不是值,则暂时忽略此操作的后果)
不幸的是,invoke\u static\u func
需要通过一个函数指针进行调用,该指针具有一个包含值类型的参数列表,因此operator()
必须进行复制才能调用函数指针(即调用invoke\u static\u func
)。让invoke\u static\u func
通过引用获取参数没有帮助,因为它仍然必须通过没有引用参数类型的函数指针来调用
invoke_static_func无法避免复制到call test(C1),这只是一个简单的按值调用-因此您需要两个副本才能使此设计正常工作
从另一个角度来解释它,用纯C来表示: 运算符()需要调用函数
func(此函数为ptr,arg\u 1,arg\u 2,arg\u 3)
。目标函数期望这些参数位于特定寄存器或特定堆栈位置,具体取决于它们在参数列表中的位置和大小
但是静态函数没有magic first'this'参数,它的签名只是func(arg_1,arg_2,arg_3)
。因此,它期望所有其他参数位于与相应成员函数不同的寄存器和/或堆栈位置。因此,您需要该副本将参数移动到正确的寄存器/堆栈位置,以符合静态函数的调用约定
这基本上意味着,在这种设计中,静态函数无法避免第二个副本
然而。。。通过一些巧妙的模板元编程,您可以在
invoke_static_func
的实现中将std::move应用于值类型参数,从而将调用开销减少到一个副本和一个移动,这几乎等同于一个副本
如果我知道这是否可行(如果可行,如何实现),我会更新这个答案
编辑 像这样的事情应该可以做到:
template <bool IsClass, class U>
struct move_if_class
{
template <typename T>
T&& operator()(const T& t) { return std::move(const_cast<T&>(t)); }
};
template <class T>
struct move_if_class<false,T>
{
T&& operator()(typename std::remove_reference<T>::type& t) { return std::forward<T>(t); }
T&& operator()(typename std::remove_reference<T>::type&& t) { return std::forward<T>(t); }
};
R invoke_static_func(P... args) const
{
return (*(closure_.GetStaticFunction()))(move_if_class<std::is_class<P>::value,P>()(args)...);
}
据我所知,代码不完整,也就是说,我无法轻松重现问题。将为传递的值创建一个副本。我怀疑中间转发使用与被调用函数相同的签名执行另一个转发。也就是说:为什么不使用
std::function
?因为std::function不支持比较:operator==()。你能试着把你的代码浓缩成一个简洁的例子,说明C++98是如何工作的,C++11版本可以做额外的复制吗?我看到在fast\u delegate\u base::operator()
中实现了完美的转发,但其他地方都没有——这是故意的吗?从我(天真的)观点来看,这似乎是错误的。@JohnZwinck我和你一样,认为这是对1998年实施的倒退。但是'98实现实际上需要3个副本,std::function(w/GCC4.6.1)也是如此。此实现通过在运算符()上使用完美转发避免了一个副本,即第一个副本,而原始副本则没有。如果传递的对象支持移动语义,则效果会更好。“让我再等一会儿:谢谢。”丹尼尔克。再仔细考虑一下,我认为有可能去掉该副本-您必须对内部函数指针的签名进行一些处理,以将所有类类型参数转换为常量左值引用,并引入一个invoke_member_func
函数,该函数具有调用目标成员函数的修改签名。这样一来,invoke_[member | static]\u func
函数就不会复制,调用目标函数时总是复制一个副本。祝你好运:-)
template <bool IsClass, class U>
struct move_if_class
{
template <typename T>
T&& operator()(const T& t) { return std::move(const_cast<T&>(t)); }
};
template <class T>
struct move_if_class<false,T>
{
T&& operator()(typename std::remove_reference<T>::type& t) { return std::forward<T>(t); }
T&& operator()(typename std::remove_reference<T>::type&& t) { return std::forward<T>(t); }
};
R invoke_static_func(P... args) const
{
return (*(closure_.GetStaticFunction()))(move_if_class<std::is_class<P>::value,P>()(args)...);
}
C1()
C1(const C1&)
C1(C1&&)
C1::test(1234)
~C1()
~C1()
~C1()