如何传递lambda, function object或std::function作为模板参数并控制它们的参数
how to pass lambda, function object or std::function as template arguments and control their arguments
我想实现一些协议作为一个类模板,raw_write和raw_read函数作为模板参数。两个原始函数都有严格定义的接口:
int raw_write(uint8_t *src, size_t len);
int raw_read(uint8_t *dst, size_t maxlen);
是否有任何方法来控制这个接口与编译错误,当有人试图传递,例如:
int raw_write2(uint16_t *src, size_t len);
我应该传递模板参数作为指定类型的对象还是作为模板实现内部实例化的类型名称?
我认为这是使用std::function
存储可调用对象(不需要使用已定义的template
类)的理想(或至少一个)解决方案:
#include <iostream>
#include <functional>
struct protocol_callbacks
{
using func_t = std::function<int(uint8_t*, size_t)>;
protocol_callbacks(func_t a_reader, func_t a_writer) :
reader(a_reader),
writer(a_writer) {}
func_t reader;
func_t writer;
};
int writer(uint8_t*, size_t) { return 0; }
int reader(uint8_t*, size_t) { return 0; }
int bad_writer(uint16_t*, size_t) { return 0; }
int main ()
{
protocol_callbacks pc1(reader, writer);
protocol_callbacks pc2([](uint8_t*, size_t) { return 0; },
[](uint8_t*, size_t) { return 0; });
//protocol_callbacks pc3(bad_writer, reader);
}
使用bad_writer
会导致编译失败(不使用bad_writer
http://ideone.com/hG7tqc和使用bad_writer
http://ideone.com/roMJgM)。
您可以使用SFINAE和一些trait类来完成此操作。
我认为最好的方法是期望有一个调用兼容的函子——这也恰好更容易。
#include <utility>
#include <type_traits>
template<typename T, bool=true>
struct raw_write_compatible: std::false_type {};
template<typename T, bool=true>
struct raw_read_compatible: std::false_type {};
template<typename T>
struct raw_write_compatible<
T,
std::is_convertable<
decltype(
std::declval<T&>()(
std::declval<uint8_t *>(),
std::declval<size_t>()
)
),
int
>::value
>: std::true_type {};
template<typename T>
struct raw_read_compatible<
T,
std::is_convertable<
decltype(
std::declval<T&>()(
std::declval<uint8_t *>(),
std::declval<size_t>()
)
),
int
>::value
>: std::true_type {};
这里的要点是,如果T
的一个实例可以用签名(uint8_t*, size_t)
求值,并且返回类型可以转换为int
,那么raw_read_compatible< T >::value
就是true
。
(顺便说一句,您的"write"函数签名可能应该使用指向const uint8_t
的指针,因为它不会修改该参数。)
你可以这样使用:
template<typename Reader, typename Writer>
typename std::enable_if<
raw_read_compatible<Reader>::value && raw_write_compatible<Writer>::value,
bool // return value of do_some_io_stuff
>::type do_some_io_stuff( Reader const& reader, Writer const& writer ) {
return true;
}
如果reader/writer可以按你想要的方式调用,和do_some_io_stuff
将匹配。
这样做的好处是,当您试图传入不兼容的lambda或函数指针或函子时,do_some_io_stuff
无法匹配,而不是匹配然后编译失败。理论上,这可以让你重写一些东西。
上述解决方案需要具有体面的c++ 11支持的编译器:例如,MSVC2012不能与上述一起工作(它在其单词中缺乏"表达式SFINAE")。
一个更简单的解决方案是只使用std::function< int(uint8_t*, size_t) >
,但这有两个代价:首先,它在每次调用时都有运行时成本(大致相当于virtual
方法调用——所以与io相比没有那么高)——真正的成本是在函数调用边界上阻塞优化。第二,在我的经验中,你可以得到一些编译失败,而不是无法匹配签名错误(我不确定c++ 11标准是否指定std::function
的"lambda"构造函数应该只在传递兼容类型时匹配,但我认为我见过失败的实现)。
std::function
解决方案的优点是它更简单,它允许将实现放在一个单独的文件中,并且意图更容易理解。
- 为什么 std::function 可以作为 std::not2 的参数?
- 传递给std::function template的template参数究竟代表什么
- 如果模板没有可变参数,则 Lambda 被推导出为 std::function
- 可变参数模板参数扩展 类型为 std::function 的类成员
- std::function<void()> 接受参数
- 在标准中,模板参数的语法在哪里定义,例如,'std::function<int(char)>'?
- C++派生类重载函数(带有 std::function 参数)不可见
- C++ 事件管理器的回调,使用 std::function 和 std:bind 以及派生类作为参数
- 将函数的参数 - 签名从使用 'std::function<T>' 转换为模板参数类型
- C++初始化 std::function 时如何将占位符绑定到引用/引用参数?
- 仅当一个参数中未使用 std::function 时,模板函数替换才有效
- 可变参数函数模板不能很好地使用 std::function 作为参数
- 将另一个类的成员函数传递到 std::function 参数中
- 如何返回用参数包定义的成员函数的std::function对象
- 了解 std::function 的晦涩模板参数
- 将 std::function* 传递给模板参数
- 构造函数模板参数推导,其中 std::function 作为参数
- 使用"std::function"和先前推断的模板参数替换失败 - 为什么?
- 使用 std::function 作为具有任意数量参数的数据成员
- 将可变参数函数参数转发到 std::function 对象