我有以下内容:
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<html ng-app = "appModule">
<table ng-controller = "AppController">
<thead>
<tr>
<th ng-repeat="head in heads">{{ head }}</th>
</tr>
</thead>
<tbody>
<tr ng-repeat="person in people">
<td ng-repeat="attr in attrs"> {{ person[attr]}} </td>
</tr>
</tbody>
</table>
</html>
哪个好,我可以做到以下几点:
struct Args;
template <typename T>
void DoStuff(T&& callback) {
// ... do stuff
MyArgs args = ...
callback(args);
}
和
void callback(const Args &);
DoStuff(callback);
和
DoStuff([](const Args &) { ... });
一切都很好,现在我想在这里进行两项改进:
强制class CallbackClass {
operator()(const Args &);
};
CallbackClass myCallbackClass;
DoStuff(myCallbackClass);
的签名始终为T
,因为在当前状态下,我可以执行以下操作:
void(const Args &)
允许(除了之前的选项)传递一个具有特定命名成员函数的对象,该函数也可以作为回调,例如:
void badCallback(Args);
DoStuff(badCallback);
有可能吗?
答案 0 :(得分:2)
您可以使用std::function
作为参数或原始指针来解决问题。
第一个版本(std::function
)略胜一筹,因为允许使用对象仿函数。
void DoStuff_fn(std::function<void(const Args&)> callback) { callback(Args{}); }
void DoStuff_raw(void (*callback)(const Args&)) { callback(Args{}); }
struct Functor {
void operator()(const Args&) {}
};
// ...
DoStuff_fn(Functor{}); // right call operator() of object
// DoStuff_raw(Functor{}); // error no conversion available
开销(优化)非常小,您可以在演示程序集中看到。无论如何,如果您需要关于这些主题的一些高级答案,最好打开一个特定的问题。
关于你的观点:
强制T的签名始终为空(const Args&amp;),因为在当前状态下我可以执行以下操作:
void badCallback(Args);
DoStuff(badCallback);
Args
可隐式转换为const Args&
,因此您无法存档。无论如何不允许使用不良演员表(我已提交DoStuff_fn
)。
void bad_callback(Args&) {}
// ...
// DoStuff_fn(bad_callback); // error! cannot cast Args& --> const Args&
问候你的问题:
允许(除了前面的选项之外)传递一个具有特定命名成员函数的对象,该函数也可以被允许作为回调
这是可能的,但有一些高级技巧。
我在这个问题中建议你采用一种非常简单的方法(利用 C ++ 17 constexpr if
)。
如果您需要更强大的功能或 C ++ 11兼容,您应该利用SFINAE
(here一个好的教程)。
相反,我的方法(保持这个问题非常简单易读):
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{});
}
}
这样,T
类型应该是带有签名void(const Args&
)可转换的仿函数,否则它应该具有perform_op
方法。
Here演示。
修改强>
如果您想利用 SFINAE ,您的方法应该是:
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{});
}
提升解决方案 使用 Boost 库:
#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{});
}
答案 1 :(得分:0)
你的第一个问题可以这样解决:
#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.
}