如何使用可变模板参数保存可变数量的参数
How to save variable number of arguments using variadic template arguments?
我想创建一个模板类,它可以为这个函数存储函数指针和参数,这样以后就可以用这个参数调用函数了。
我想写这篇文章,不要依赖于参数类型或数量。
以下是使用c++11的可变模板的想法:
template<class T, typename... Params>
class LazyEvaluation {
private:
// Function to be invoked later
T (*f)(Params...);
// Params for function f
Params... storedParams; // This line is not compilable!
bool evaluated;
T result;
public:
// Constructor remembers function pointer and parameters
LazyEvaluation(T (*f)(Params...),Params... params)
: f(f),
storedParams(params) //this line also cannot be compiled
{}
// Method which can be called later to evaluate stored function with stored arguments
operator T&() {
// if not evaluated then evaluate
if (! evaluated) {
result = f(storedParams...);
evaluated = true;
}
return result;
}
}
如果可能的话,我希望至少这个类类型的公共接口是安全的。尽管获得这份工作至少在某种程度上更重要。
我设法以某种方式保存了可变数量的参数。但我没能把它们传递给函数f。我会把它写为答案,但我希望你在看到我丑陋的不起作用的尝试之前,先想想自己的解决方案。
我正在使用Microsoft Visual C++编译器2012年11月CTP(v120_CTP_Nov2012)编译上面的代码,但最好是存在独立于编译器的解决方案。
感谢
以下是我试图解决它的方法:
参数包可以重复展开,并保存每个参数。函数存储应该做到这一点。它使用一个(两次重载)辅助函数。
template<typename T>
void storeHelperFunction(void*& memory, T last) {
*((T*)memory) = last;
memory = (void*)((char*)memory + sizeof(T));
}
template<typename T, typename... Params>
void storeHelperFunction(void*& memory, T first, Params... rest) {
storeHelperFunction(memory, first);
storeHelperFunction(memory, rest...);
}
template<typename... Params>
void store(void* memory, Params... args) {
// Copy of pointer to memory was done when passing it to this function
storeHelperFunction(memory, args...);
}
函数存储获取一个指向内存的指针,其中应该保存可变数量的参数。
指针可以指向一些动态分配的内存,也可以指向大小等于sizeof...(Params)
的结构。这种完全具有任何所需大小的结构都可以使用模板元编程来构建:
template <int N>
struct allocatorStruct {
char byte1;
allocatorStruct<N-1> next;
};
template <>
struct allocatorStruct<1> {};
我不知道标准是怎么说的,也不知道微软以外的其他编译器是如何编译的。但使用我的编译器,对于任何大于或等于1的N,(allocaterStruct)的大小都等于N。
因此,CCD_ 2具有与Params相同的大小。
创建与Params大小相同的东西的另一种方法是使用类型char [sizeof...(Params)]
。这样做的缺点是,当您尝试将此数组作为参数传递时,编译器只传递指向此数组的指针。这就是为什么最好使用allocatorStruct<sizeof...(Params)>
。
现在的主要想法是:
保存函数时,我们可以将其强制转换为:T (*)(allocatorStruct<sizeof...(Params)>)
。当保存函数的参数时,我们可以将它们保存到类型为allocatorStruct<sizeof...(Params)>
的结构中。
参数的大小相同。尽管函数指针与函数的类型有关,但所指向的函数将正确获取其数据。
至少我希望如此。根据调用约定,我预计传递的参数可能会被重新排序或出错,因为从左到右保存参数和从右到左传递参数之间存在差异。但事实并非如此。使用__cdecl调用约定时,只传递了第一个参数,而丢失了另一个参数。使用其他调用约定时,程序停止工作。
我没有花太多时间调试它并在内存中查找数据(在堆栈上)。这至少是正确的做法吗?
只需使用lambda表达式
// Some function.
int add(int a, int b) {
return a + b;
}
auto lazyFunc = [] { return add(1, 2); };
std::cout << lazyFunc() << std::endl; // Evaluate function and output result.
如果您真的想创建一个使用可变模板只对函数求值一次(延迟)的类,您可以在以下代码中执行类似操作。
我还创建了这样的类,即不必每次参数更改时都创建一个新实例。我使用std::tuple
来存储给定的参数,并与以前给定的参数进行比较。如果参数不同,则将重新评估函数。
函数是使用std::function
包装器传递和存储的,所以我不必使用原始函数指针(当然)。
#include <iostream>
#include <functional>
#include <utility>
#include <tuple>
template <typename T>
class LazyEvaluation {};
template <typename ReturnType, typename... Params>
class LazyEvaluation<ReturnType(Params...)> {
private:
std::function<ReturnType(Params...)> func_;
ReturnType result;
std::tuple<Params...> oldParams; // Contains the previous arguments.
public:
explicit LazyEvaluation(std::function<ReturnType(Params...)> func)
: func_(std::move(func)) {}
template <typename... Args>
ReturnType operator() (Args&&... args) {
auto newParams = std::make_tuple(std::forward<Args>(args)...);
// Check if new arguments.
if (newParams != oldParams) {
result = func_(std::forward<Args>(args)...);
oldParams = newParams;
std::cout << "Function evaluated" << std::endl;
}
std::cout << "Returned result" << std::endl;
return result;
}
};
int main() {
auto f = [] (int a, int b) {
return a + b;
};
// Specify function type as template parameter.
// E.g. ReturnType(Param1Type, Param2Type, ..., ParamNType)
LazyEvaluation<int(int, int)> ld(f);
std::cout << ld(1, 2) << std::endl;
std::cout << ld(1, 2) << std::endl;
std::cout << ld(3, 4) << std::endl;
}
输出:
Function evaluated
Returned result
3
Returned result
3
Function evaluated
Returned result
7
给定形成可变索引包的标准机制:
template <std::size_t... I> struct index_sequence {};
template <std::size_t N, std::size_t... I>
struct make_index_sequence : public make_index_sequence<N-1, N-1, I...> {};
template <std::size_t... I>
struct make_index_sequence<0, I...> : public index_sequence<I...> {};
以及使用未封装的元组参数调用函数:
template <typename Function, typename... Types, std::size_t... I>
auto apply_(Function&& f, const std::tuple<Types...>& t, index_sequence<I...>)
-> decltype(std::forward<Function>(f)(std::get<I>(t)...)) {
return std::forward<Function>(f)(std::get<I>(t)...);
}
template <typename Function, typename... Types>
auto apply(Function&& f, const std::tuple<Types...>& t)
-> decltype(apply_(f, t, make_index_sequence<sizeof...(Types)>())) {
return apply_(f, t, make_index_sequence<sizeof...(Types)>());
}
这相当简单:
template<typename Function, typename... Params>
class LazyEvaluation {
private:
typedef decltype(std::declval<Function>()(std::declval<Params>()...)) result_type;
// Function to be invoked later
Function f;
// Params for function f
std::tuple<Params...> storedParams;
mutable bool evaluated;
union {
std::aligned_storage<sizeof(result_type)> space;
mutable result_type result;
};
// Method which can be called later to evaluate stored function with stored arguments
void evaluate() const {
// if not evaluated then evaluate
if (! evaluated) {
new (&result) result_type{apply(f, storedParams)};
evaluated = true;
}
}
public:
// Constructor remembers function pointer and parameters
LazyEvaluation(Function f, Params... params)
: f(std::move(f)),
storedParams(std::move(params)...),
evaluated(false)
{}
~LazyEvaluation() {
if (evaluated)
result.~result_type();
}
operator result_type&() {
evaluate();
return result;
}
operator const result_type& () const {
evaluate();
return result;
}
};
template <typename Function, typename... Params>
LazyEvaluation<Function, Params...>
make_lazy(Function&& f, Params&&... params) {
return {std::forward<Function>(f), std::forward<Params>(params)...};
}
我使用了并集和放置new
来存储计算结果,这样它就不需要是默认的可构造类型,还使用了一些mutable
技巧,这样const LazyEvaluator
就可以转换为非常数实例。
- 是否可以保存带有参数的函数指针以供以后使用?
- 如何将 std::string 作为构造函数参数传递,并将其保存的 C 字符串存储在 void 指针中?
- 无法绑定可变参数函数并保存到 std::函数
- GoogleMock:如何保存参数,以便在模拟上的下一次调用中使用
- put_money是否按值或引用保存其参数
- 如何定义可以保存可变参数模板参数的类
- 在参数包中获取函数指针的返回类型,并将其保存为与其他参数连接的元组
- 是否可以在数组中保存具有不同参数的成员函数?
- 一个用于保存模板专业参数的结构
- boost::绑定在存储时不保存部分参数
- 定义变量以保存内部参数
- 这很快:通过函数返回值或将值保存在参数/参数中
- 常量参数与右值参数传递,当内部保存到类私有变量时
- 保存命令行参数并对其进行类型转换
- C++函数通过strlen()运行'string'类型参数,将新值保存到虚拟类实例,将虚拟类传递给新函数。为什么?
- 如何使用可变模板参数保存可变数量的参数
- C++谷歌模拟SaveArg:如何保存指针参数
- 在内部结构的定义中使用保存函数的类成员变量,这些内部结构将用作unordered_map对象的模板参数
- 如何使void*参数在函数中保存其局部结果,该函数使用struct *调用
- 具有指针参数的类函数未保存到向量