C++ C++;重载具有特定签名的模板
我有以下资料:C++ C++;重载具有特定签名的模板,c++,templates,overloading,C++,Templates,Overloading,我有以下资料: struct Args; template <typename T> void DoStuff(T&& callback) { // ... do stuff MyArgs args = ... callback(args); } void callback(const Args &); DoStuff(callback); void badCallback(Args); DoStuff(badCallback);
struct Args;
template <typename T>
void DoStuff(T&& callback) {
// ... do stuff
MyArgs args = ...
callback(args);
}
void callback(const Args &);
DoStuff(callback);
void badCallback(Args);
DoStuff(badCallback);
以及:
以及:
一切都很好,现在我想在这里做两个改进:
T
的签名始终为void(const Args&)
,因为在当前状态下,我可以执行以下操作:
struct Args;
template <typename T>
void DoStuff(T&& callback) {
// ... do stuff
MyArgs args = ...
callback(args);
}
void callback(const Args &);
DoStuff(callback);
void badCallback(Args);
DoStuff(badCallback);
class NamedCallback {
void PerformCallback(const Args &);
};
NamedCallback myNamedCallback;
DoStuff(myNamedCallback);
有可能吗?您的第一个问题可以这样解决:
#include <type_traits>
#include <tuple>
// the generic signature falls back on the signature of the call operator if present
template <class C>
struct signature : signature< decltype( &std::decay_t<C>::operator() ) > {};
// pointer to member function fall back on the plain function signatures
template < class C , typename Result , typename... Args >
struct signature< Result (C::*)(Args...) > : signature< Result ( Args... ) > {};
template < class C , typename Result , typename... Args >
struct signature< Result (C::*)(Args...) const > : signature< Result ( Args... ) > {};
// pointer and references to free function fall back on the plain function signatures
template < typename Result , typename... Args >
struct signature< Result (*)(Args...) > : signature< Result ( Args... ) > {};
template < typename Result , typename... Args >
struct signature< Result (&)(Args...) > : signature< Result ( Args... ) > {};
// actual implementation just for pure function signature types
template < typename Result , typename... Args >
struct signature< Result ( Args... ) >
{
static constexpr auto num_args = sizeof...(Args);
template< size_t n >
using argument = typename std::tuple_element< n, std::tuple<Args...> >;
using result_type = Result;
};
template <typename Callable, size_t N >
using argument_t = typename signature<Callable>::template argument<N>::type;
// -------------------------------------------
struct Args {};
template <typename T> // could use enable_if as well
// , typename = std::enable_if_t<std::is_same_v<argument_t<T,0>,const Args&>>>
void DoStuff(T&& callback) {
static_assert(std::is_same_v<argument_t<T,0>,const Args&>, "Callback has the wrong signature");
// ... do stuff
Args args = {};
callback(args);
}
void callback(const Args &) {}
struct CallbackClass {
void operator()(const Args &){}
};
int main()
{
DoStuff(callback);
DoStuff(CallbackClass());
DoStuff([](const Args &) { });
// DoStuff([](Args) { }); // won't compile, triggers static assertion informing the user about the error.
}
#包括
#包括
//通用签名依赖于呼叫接线员的签名(如果存在)
模板
结构签名:签名{};
//指向成员函数的指针回退到普通函数签名上
模板
结构签名<结果(C::*)(Args…>:签名<结果(Args…>{};
模板
结构签名<结果(C::*)(Args…)常量>:签名<结果(Args…>{};
//指向自由函数的指针和引用返回到普通函数签名上
模板
结构签名<结果(*)(Args…>):签名<结果(Args…>{};
模板
结构签名<结果(&)(参数…>:签名<结果(参数…>{};
//仅针对纯函数签名类型的实际实现
模板
结构签名<结果(参数…>
{
静态constexpr auto num_args=sizeof…(args);
模板
使用参数=typename std::tuple\u元素
关于第二个问题,可以使用成员函数检测技术解决。有许多版本,例如。简单方法
您可以使用std::function
作为参数或原始指针来解决这个问题
第一个版本(std::function
)稍好一些,因为它允许使用对象functor
Args
可隐式转换为const Args&
,因此不可能进行归档。无论如何,不允许使用坏类型转换(使用我介绍的DoStuff\u fn
)
高级方法
关于你的问题:
允许(除了前面的选项之外)传递具有特定命名成员函数的对象,该函数也可以作为回调函数
这是可能的,但需要一些高级技巧
在这个问题上,我可以建议您使用一种非常简单的方法(利用C++17constexpr if
)
如果您需要更健壮或与C++11兼容的东西,您应该利用SFINAE
(一个很好的教程)
取而代之的是我的方法(保持这个问题的可读性和简单性):
增强解决方案
使用Boost库:
#包括
使用Signature=std::函数;
模板
typename std::enable_if::type
DoStuff_adv(T&函子){
函子(Args{});
}
BOOST\u TTI\u具有成员功能(执行操作)
模板
typename std::启用\u如果<
是否有成员函数执行值>类型
DoStuff_adv(T&函子){
函子.perform_op(Args{});
}
为什么不使用std::function
而不是原始函数指针?@user0042-什么原始函数指针?此模板接受任何可调用类型。因为我不喜欢每次想DoStuff()时构造新对象的想法
,一个好问题是,std::function
会产生多少开销?@Vraiment-否,这是一个过早优化的问题。正确的问题是“我是否需要std::function的类型擦除功能,还是在代码可能膨胀的情况下使我的整个函数成为一个足够好的模板?”?。@StoryTeller说得好,我想你是对的。这解决了我的第一个问题,通过使用std::function
我可以强制执行回调签名。这并没有回答问题的第2项。@skypjack为什么不呢?使用SFINAE或boost示例,你可以调用DoStuff\u adv(myNamedCallback)
。我甚至用C++17True提供了一个例子。无论如何,我怀疑提出这样一个问题的用户(或未来对这个问题感兴趣的读者)几乎不知道如何实现有一个特殊的方法。
。你只是通过注入一个新方法来解决一个疑问。我的两分钱。
void badCallback(Args);
DoStuff(badCallback);
void bad_callback(Args&) {}
// ...
// DoStuff_fn(bad_callback); // error! cannot cast Args& --> const Args&
using Signature = std::function<void(const Args&)>;
template <typename T>
void DoStuff_adv(T&& functor) {
if constexpr (std::is_convertible<T, Signature>::value) {
functor(Args{});
} else {
functor.perform_op(Args{});
}
}
using Signature = std::function<void(const Args&)>;
template <typename T>
typename std::enable_if<std::is_convertible<T, Signature>::value>::type
DoStuff_adv(T&& functor) {
functor(Args{});
}
template <typename T>
typename std::enable_if<has_special_method<T>::value>::type
DoStuff_adv(T&& functor) {
functor.perfom_op(Args{});
}
#include <boost/tti/has_member_function.hpp>
using Signature = std::function<void(const Args&)>;
template <typename T>
typename std::enable_if<std::is_convertible<T, Signature>::value>::type
DoStuff_adv(T&& functor) {
functor(Args{});
}
BOOST_TTI_HAS_MEMBER_FUNCTION(perform_op)
template <typename T>
typename std::enable_if<
has_member_function_perform_op<void (T::*)(const Args&)>::value>::type
DoStuff_adv(T&& functor) {
functor.perform_op(Args{});
}