在可变进模板展开中增加int值的安全方法是什么?
What is a safe way to increment an int in a variadic template expansion?
我正在尝试实现一个c++ 11包装器,围绕用C编写的SQL库。C库具有从需要列索引的SQL语句获取不同数据类型的单独函数。下面是一个简单的方法的原型,但它有一个严重的缺陷:它依赖于参数执行的顺序,这是不安全的(也可能有编译器错误,还没有测试过)。
问题:在可变模板扩展中安全地增加变量的方法是什么?
template< typename... ColumnTypes >
void SQLStatement::execute( std::function< void( ColumnTypes... ) > rowCallback ){
while( this->nextRow() ){
int column = 0;
rowCallback( this->getColumn< ColumnTypes >( column++ )... );
// unreliable increment ^
}
}
template< typename T >
T SQLStatement::getColumn( const int columnIdx ){}
template<>
inline int SQLStatement::getColumn< int >( const int columnIdx ){
return sql_library_column_int( this->nativeHandle, columnIdx );
}
// Other getColumn specializations here...
这似乎行得通。你只需要调整一些东西:
#include <functional>
#include <iostream>
#include <cstddef>
void foo(int a, float b, int c) {
std::cout << a << ", " << b << ", " << c << std::endl;
}
template<typename T>
T getColumn(int index) {
return T(index);
}
template<size_t... indexes>
struct index_tuple {};
template<size_t head, size_t... indexes>
struct index_tuple<head, indexes...> {
typedef typename index_tuple<head-1, head-1, indexes...>::type type;
};
template<size_t... indexes>
struct index_tuple<0, indexes...> {
typedef index_tuple<indexes...> type;
};
template<typename... Args>
struct make_index_tuple {
typedef typename index_tuple<sizeof...(Args)>::type type;
};
template<typename... ColumnTypes, size_t... indexes>
void execute(const std::function<void(ColumnTypes...)> &callback, index_tuple<indexes...>) {
// this should be done for every row in your query result
callback(getColumn<ColumnTypes>(indexes)...);
}
template<typename... ColumnTypes>
void execute(const std::function<void(ColumnTypes...)> &callback) {
execute(
callback,
typename make_index_tuple<ColumnTypes...>::type()
);
}
int main() {
std::function<void(int, float, int)> fun(foo);
execute(fun);
}
演示。请注意,函数foo
仅用于显示索引正确递增,就像getColumn
中的return T(index);
一样。
虽然mfontanini的解决方案工作良好,因为它在编译时执行递增列索引的计算,但我认为值得指出的是,对于如何在可变包展开中递增int的问题也有一个直接的答案。(不幸的是,由于一个bug,它似乎不能在GCC上工作,请参阅最后的警告。)
答案是基于这样一个事实:虽然函数调用中参数的求值是无序的,但列表初始化中参数的求值不是:
因此,如果您将函数调用转换为基于大括号初始化列表的内容,您将获得所需的效果:(§8.5.4/4)在带括号的初始化列表中,初始化子句,包括包展开(14.5.3)产生的任何初始化子句,按它们出现的顺序求值。也就是说,在初始化器列表的逗号分隔列表中,与给定初始化器子句相关的每个值计算和副作用都排在与它后面的任何初始化器子句相关的每个值计算和副作用之前。
[注:无论初始化的语义如何,这个求值顺序都保持不变;例如,当初始化器列表的元素被解释为构造函数调用的参数时,即使调用的参数通常没有排序约束,它也适用。
rowCallback(std::tuple<ColumnTypes...> { getColumn<ColumnTypes>(column++)... });
使用列表初始化std::tuple
(注意括号{ ... }
),因此column++
的副作用将按照从左到右的顺序执行。
如果像上面那样写,这意味着你需要改变rowCallback()
使它接受一个std::tuple
而不是一个参数列表。如果您不喜欢这样,您可以创建一个单独的模板函数call_on_tuple(fun,tup)
,该函数将根据展开元组tup
所产生的参数调用任何函数fun
。我曾经在这里描述过如何做到这一点,或者如果你喜欢,你可以使用我的GitHub存储库中的rlxutil::call_on_tuple
。
你的execute
函数看起来像这样:
template <typename... ColumnTypes>
void execute(function<void(ColumnTypes...)> rowCallback)
{
using std::tuple;
using rlxutil::call_on_tuple;
int column = 0;
call_on_tuple(rowCallback,
tuple<ColumnTypes...> { getColumn<ColumnTypes>(column++)... });
}
警告:这在GCC中不能像预期的那样工作。我相信这是因为这里报告的错误:http://gcc.gnu.org/bugzilla/show_bug.cgi?id=51253.
- 从不同线程使用int64的不同字节安全吗
- 为什么在全局范围内使用"extern int a"似乎不行?
- 为表示一个或多个操作的C++函数的int参数寻找类型安全的替换
- 为什么我的 std::atomic<int> 变量不是线程安全的?
- 最有效的安全方法将 std::map<int, std::shared_ptr> 转换为 std::<Base>map<int, std::shared_ptr<D
- 将无符号的 int 与 std::string::size_type 进行比较是否安全
- 从std::round转换为int是否安全
- 读取即将在没有同步的情况下同时增加的int是安全的吗?
- 有没有一种安全的方法可以迭代 std::unique_ptr<int[]>?
- 将 -1 分配给无符号 int 以获得最大值是否安全
- 在C++中递增未初始化的 int 是否安全
- 我可以制作一个线程安全的 std::atomic<vector<int>>吗?
- 将矢量<int>分配给矢量是否安全<double>?
- 使用 shared_ptrs 对<无符号 int、boost::any> 类型的通用容器的线程安全实现
- 在unsigned int和signed int之间进行安全转换
- 将intptr_t传递给期望int类型的函数是否安全?
- 如果允许将int赋值给float(反之亦然),是否不类型安全?
- 在可变进模板展开中增加int值的安全方法是什么?
- C++像这样将数组从char转换为unsigned int正确且安全吗
- 在 C++03 中通过 int 运输双精度是否安全