C++使用可变模板编译特定于时间的函数
C++ Compile time specific functions with variadic templates
我目前正在开发一个函数抽象库。我想把一个多参数函数抽象成一个单参数函数,它是一个索引。可选地,我想传递一个谓词对象,该对象决定它是否应该运行代码。
using FnType = std::function<void(const unsigned long)>;
template<typename Fn, typename Predicate, typename ... Args>
const FnType make_fn(const Fn &fn, const Predicate &predicate, Args ... args) {
return [&](const unsigned long idx) {
if(predicate(idx)) fn(idx, args ...);
};
}
template<typename Fn, typename ... Args>
const FnType make_fn(const Fn &fn, Args ... args) {
return [&](const unsigned long idx) {
fn(idx, args ...);
};
}
当使用谓词时,此代码非常有效。
std::vector<double> data(10);
auto fn = [&](unsigned idx, double d) { data[idx] *= d; };
auto pred = [](unsigned long idx) { return idx % 2 == 0; };
FnType new_fn = make_fn(fn, pred, 2.0);
但是当我尝试不传递谓词时
FnType new_fn2 = make_fn(fn, 2.0);
它返回以下编译时错误消息:
called object type 'double' is not a function or function pointer
if(predicate(idx)) fn(idx, args ...);
^~~~~~~~~
有没有可能说编译器应该使用第二个不使用任何谓词的函数?
让我们看看您的签名:
template<typename Fn, typename Predicate, typename ... Args>
const FnType make_fn(const Fn&, const Predicate&, Args ...); // (1)
template<typename Fn, typename ... Args>
const FnType make_fn(const Fn&, Args ... ) // (2)
当我们与通话时
make_fn(fn, 2.0);
Predicate
没有什么特别之处——它只是另一种类型。因此,当我们进行过载解析时,我们有两个完全匹配。我们解析变参数的方法是(1)
比(2)
更专业——因为(1)
至少需要2个参数,(2)
至少需要1个参数,所以前者更具体。这就是为什么我们称前者为。编译器从签名中不知道Predicate
是什么意思。
最简单的解决方案是简单地重命名一个或另一个函数-您真的需要重载它们吗?拥有make_fn()
和make_predicate_fn()
。
或者,我们可以使用SFINAE通过强制Predicate
是可调用的来改变这一点:
template <typename Fn, typename Predicate, typename... Args,
typename = decltype(std::declval<Predicate>()(0u))>
const FnType make_fn(const Fn&, const Predicate&, Args ...);
这将使(1)
不适合您的呼叫,因此(2)
将是首选。尽管这还有一个缺点,如果你不想以这种方式使用谓词,而是简单地使用一些第一个参数是谓词的函数,该怎么办?会发生错误的事情。
T.C.在评论中提出的一个更好的解决方案是只引入一个空标签类型:
struct predicate_tag_t { };
constexpr predicate_tag_t predicate_tag{};
以及标签的过载:
template <typename Fn, typename Predicate, typename... Args>
FnType make_fn(const Fn&, predicate_tag_t, const Predicate&, Args&&... );
template <typename Fn, typename... Args>
FnType make_fn(const Fn&, Args&&... );
这样你的例子就变成了:
FnType new_fn = make_fn(fn, predicate_tag, pred, 2.0);
FnType new_fn2 = make_fn(fn, 2.0);
当你有时,你在两个make_fn
中都有悬空引用
const FnType make_fn(const Fn &fn, Args ... args) {
return [&](const unsigned long idx) { ... }
}
您正在复制所有参数,然后保留对所有参数的引用。但一旦make_fn
完成,所有的args
都将超出范围。你需要复制它们——所以[=]
。
将上层函数更改为
template<typename Fn, typename Predicate, typename ... Args
, typename = std::enable_if_t<sizeof ... (Args) != 0> >
const FnType make_fn(const Fn &fn, const Predicate &predicate, Args ... args) {
return [=](const unsigned long idx) {
if(predicate(idx)) fn(idx, args ...);
};
}
这确保了参数包中至少有一个成员,然后在过载解决过程中选择第二个函数。
需要注意的更多事项:
最好将返回类型设置为
auto
——您仍然可以将其配置为std::function
,但如果您简单地调用它,则需要更少的开销。可能您不想按值传递
Args ...
中的通用参数,所以最好编写Args const& ... args
。
- C++为构建时间获取QDateTime的可靠方法
- 从持续时间构造std::chrono::system_clock::time_point
- 向量 <int> a {N, 0} 和 int arr a[N] = {0} 的时间复杂度有什么区别
- while循环中while循环的时间复杂度是多少
- 使用简单类型列表实现的指数编译时间.为什么
- 是否可以在编译时初始化数组,以便在运行时不会花费时间?
- 在已经使用Git的情况下减少编译时间
- 有没有一种方法可以创建一个带有哈希表的数据库,该哈希表具有恒定时间查找功能
- 为什么字符串比较的 == 运算符相对于任一字符串长度线性时间(似乎)?
- 长度库,其实现方式类似于C++时间的计时
- 使用不同的整数组装字符数组以类似于日期和时间
- C++:读取.BMP文件时出现问题;文件结束时间早于预期
- 升压C 的时期时间/时间格式
- 如何在C 11中找到持续时间(时间)单位
- C++使用可变模板编译特定于时间的函数
- c++使运行时间独立于平台
- 如何解析带有日期-时间+时间偏移的字符串到boost::posix_time::ptime
- 测试一个方法的私有成员是否依赖于时钟时间
- Boost:如何处理依赖于时间的线程操作
- 如何制作自定义MSBuild目标's的执行依赖于文件's的时间戳