这就是我想要达到的界面:
Statement select("SELECT * FROM People WHERE ID > ? AND ID < ?");
select.execute(1462, 1477, [](int ID, std::string const& person, double item1, float item2){
std::cout << "Got Row:"
<< ID << ", "
<< person << ", "
<< item1 << ", "
<< item2 << "\n";
});在哪里“?”在select字符串中,变量参数列表与运行时的1462, 1477匹配。
这是类的定义:
class Statement
{
public:
Statement(std::string const&);
template<class Action, class ...Args>
void execute(Args... param, Action action);
};不幸的是,这会产生一个错误:
test.cpp:133:12:错误:对“执行”的调用没有匹配的成员函数 select.execute(1462,1477,int ID, std::string const& person, double item1, float item2{ ~^~ test.cpp:86:14:注意:候选模板被忽略:无法推断模板参数'Action‘ 无效执行(Args..。行动) ~^ 1错误生成。
但是,如果我稍微修改函数定义(下面),它就会编译得很好。
class Statement
{
public:
Statement(std::string const&);
template<class Action, class ...Args>
void execute(Action action, Args... param);
// ^^^^^^^ Move action to the front.
};
// Also changed the call by moving the lambda to the first argument.我知道变量参数列表的语法糖,但是我想把变量参数列表放在第一位。有什么技巧可以帮助编译器正确地推断var arg列表吗?
发布于 2014-04-05 20:33:48
最后,我决定使用std::tuple作为参数,并隐藏参数展开:
现在的用法是:
select.execute(
Bind(1462, 1477),
[](int ID, std::string const& person, double item1, float item2){
std::cout << "Got Row:"
<< ID << ", "
<< person << ", "
<< item1 << ", "
<< item2 << "\n";
});然后我定义了:
template<typename ...Args>
inline std::tuple<Args...> Bind(Args... args) { return std::tuple<Args...>(args...);}类被重新定义如下:
class Statement
{
public:
Statement(std::string const&);
template<class Action, class ...Args>
void execute(std::tuple<Args...> const& param, Action action);
};发布于 2014-03-25 14:59:57
这有点难看,但您可以使用元组:
#include <iostream>
#include <string>
#include <tuple>
template<int... Is>
struct integer_sequence {};
template<int N, int... Is>
struct make_integer_sequence : make_integer_sequence<N-1, N-1, Is...> {};
template<int... Is>
struct make_integer_sequence<0, Is...> : integer_sequence<Is...> {};
class Statement
{
private:
std::string foo;
public:
Statement(std::string const& p)
: foo(p)
{}
template<class ...Args>
void execute(Args... param)
{
execute_impl(make_integer_sequence<sizeof...(Args)-1>{}, param...);
}
template<int... Is, class... Args>
void execute_impl(integer_sequence<Is...>, Args... param)
{
std::get<sizeof...(Args)-1>(std::tie(param...))
(std::get<Is>(std::tie(param...))..., foo);
}
};用法示例:
int main()
{
Statement s("world");
s.execute("hello", ", ",
[](std::string const& p1, std::string const& p2,
std::string const& p3)
{ std::cout << p1 << p2 << p3; });
std::cout << "\nEND\n";
}这里有另一种解决方案,稍微不那么难看,但更冗长:
#include <iostream>
#include <string>
#include <tuple>
template<class Tuple, class...>
struct pop_back;
template<class T, class... Ts, class... Us>
struct pop_back<std::tuple<T, Ts...>, Us...>
: pop_back<std::tuple<Ts...>, Us..., T>
{};
template<class T, class... Us>
struct pop_back<std::tuple<T>, Us...>
{
using type = std::tuple<Us...>;
};
class Statement
{
private:
std::string foo;
public:
Statement(std::string const& p)
: foo(p)
{}
template<class ...Args>
void execute(Args... param)
{
helper<typename pop_back<std::tuple<Args...>>::type>
::execute(param..., foo);
}
template<class T>
struct helper;
template<class... Args>
struct helper< std::tuple<Args...> >
{
template<class Action>
static void execute(Args... param, Action action, std::string foo)
{
action(param..., foo);
}
};
};下面是一个简短的is_callable特性,它允许一个static_assert来获得更好的错误消息:
template<class F, class... Args>
struct is_callable
{
template<class F1>
static auto test(int)
-> decltype( std::declval<F1>() (std::declval<Args>()...),
std::true_type{} );
template<class F1>
static std::false_type test(...);
constexpr static auto value = decltype(test<F>(0))::value;
};例如:
template<int... Is, class... Args>
void execute_impl(integer_sequence<Is...>, Args... param)
{
auto& action = std::get<sizeof...(Args)-1>(std::tie(param...));
auto param_tuple = std::tie(param...);
static_assert(is_callable<decltype(action),
typename std::tuple_element<Is,
decltype(param_tuple)>::type...,
decltype(foo)>::value,
"The action is not callable with those argument types.");
action(std::get<Is>(param_tuple)..., foo);
}发布于 2014-03-25 15:03:09
如果参数包不在函数调用的末尾,则无法推导它们。
引用标准,14.8.2.1 Deducing template arguments from a function call
最后参数包的规则:
对于在参数声明列表末尾出现的函数参数包,调用的每个剩余参数的类型A与函数参数包的声明符-id的类型P进行比较。每个比较都会为由函数参数包展开的模板参数包中的后续位置推导模板参数。
其他:
对于没有出现在参数声明列表末尾的函数参数包,参数包的类型是一个非推导的上下文。
https://stackoverflow.com/questions/22636151
复制相似问题