用另一个的内容和额外的元素初始化一个 C++ (11) std::vector
Initialize a C++ (11) std::vector with the content of another one and extra elements
我需要一种 "elegant" 方法来在声明阶段使用另一个向量的内容和一些额外元素来初始化一个向量。
我要解决的是:
让我们考虑以下带有初始化的(示例)声明:
const std::vector<std::string> c90_types = {
"char",
"signed char",
"unsigned char",
"short",
"unsigned short",
"int",
"unsigned int",
"long",
"unsigned long",
"float",
"double",
"long double"
};
const std::vector<std::string> c99_types = {
"char",
"signed char",
"unsigned char",
"short",
"unsigned short",
"int",
"unsigned int",
"long",
"unsigned long",
"float",
"double",
"long double",
"long long",
"unsigned long long",
"intmax_t",
"uintmax_t"
};
如您所见,c99_types
有一个子集恰好是 c90_types
。我想避免需要更改子集然后手动更改 "superset" 的情况,只是为了避免可能引入错误的额外步骤:)
附带说明一下,我不想编写如下代码:
second.insert(second.begin(), first.begin(), first.end());
second.push_back(something);
对此有什么好的、干净的解决方案吗?
使用额外的代码,您可能仍然有 const 向量:
std::vector<std::string> make_c99_type()
{
auto res = c90_types;
const std::vector<std::string> extra_c99_types = {
"long long",
"unsigned long long",
"intmax_t",
"uintmax_t"
};
res.insert(res.end(), extra_c99_types.begin(), extra_c99_types.end());
return res;
}
const std::vector<std::string> c99_types = make_c99_type();
你可以先定义最大的向量(这里是c99_types),然后用最大的向量构造其他向量
这是一个例子:
const vector<int> a{1,2,3,4};
const vector<int> b{begin(a), begin(a)+2}; // b is {1,2}
所以你可以这样写:
const std::vector<std::string> c99_types = {
"char",
"signed char",
"unsigned char",
"short",
"unsigned short",
"int",
"unsigned int",
"long",
"unsigned long",
"float",
"double",
"long double",
"long long",
"unsigned long long",
"intmax_t",
"uintmax_t"
};
const std::vector<std::string> c90_types{begin(c99_types), begin(c99_types)+12};
选项 1:std::array
这可能可以清理和改进很多,但它至少是一个起点(它使用 Jonathan Wakely's redi::index_tuple
:
template<typename T, std::size_t N, unsigned... I, typename ...U>
inline auto
append_array_helper(const std::array<T, N>& array, redi::index_tuple<I...>, U&&... elements) -> std::array<T, N + sizeof...(elements)>
{
return std::array<T, N + sizeof...(elements)>{ std::get<I>(array)..., std::forward<U>(elements)... };
}
template<typename T, std::size_t N, typename ...U>
inline auto
append_array(const std::array<T, N>& array, U&&... elements) -> std::array<T, N + sizeof...(elements)>
{
return append_array_helper(array, typename redi::make_index_tuple<N>::type(), std::forward<U>(elements)...);
}
const std::array<std::string, 12> c90_types = {
"char",
"signed char",
"unsigned char",
"short",
"unsigned short",
"int",
"unsigned int",
"long",
"unsigned long",
"float",
"double",
"long double"
};
const std::array<std::string, 16> c99_types = append_array(
c90_types,
"long long",
"unsigned long long",
"intmax_t",
"uintmax_t"
);
如果不想指定数组大小,可以使用以下方法:
template<typename T, typename... U>
constexpr auto make_array(U&&... elements) -> std::array<T, sizeof...(elements)>
{
return { std::forward<U>(elements)... };
}
const auto c90_types = make_array<std::string>(
"char",
"signed char",
"unsigned char",
"short",
"unsigned short",
"int",
"unsigned int",
"long",
"unsigned long",
"float",
"double",
"long double"
);
...
选项 2:宏
不是我最喜欢的,但它简单易懂,易于编辑:
#define C90_TYPES \
"char", \
"signed char", \
"unsigned char", \
"short", \
"unsigned short", \
"int", \
"unsigned int", \
"long", \
"unsigned long", \
"float", \
"double", \
"long double"
#define C99_TYPES \
C90_TYPES, \
"long long", \
"unsigned long long", \
"intmax_t", \
"uintmax_t"
const std::vector<std::string> c90_types = {
C90_TYPES
};
const std::vector<std::string> c99_types = {
C99_TYPES
};
您可以使用 boost::join
:
#include <vector>
#include <boost/range/join.hpp>
const std::vector<std::string> c90_types = {
"char",
"signed char",
"unsigned char",
"short",
"unsigned short",
"int",
"unsigned int",
"long",
"unsigned long",
"float",
"double",
"long double"
};
auto range = boost::join(c90_types, std::vector<std::string>{
"long long",
"unsigned long long",
"intmax_t",
"uintmax_t"
});
const std::vector<std::string> c99_types(range.begin(), range.end());
有一个叫做 "I want to initialize a const variable with something elaborate." 的技巧在 C++11 中成为可能,无耻地从 Javascript 中偷走了。
const std::vector<std::string> c90_types = {
"char",
// and so on, and so forth....
};
const std::vector<std::string> c99_types = ([&](){
const auto additional_types = { // initializer_list<const char *>, but it does not matter.
"long long",
"unsigned long long",
"intmax_t",
"uintmax_t"
};
std::vector<std::string> vec{c90_types};
vec.insert(vec.end(), additional_types.begin(), additional_types.end());
return vec;
})();
将您的初始化逻辑打包到一个未命名的 lambda 中,并立即调用它,复制初始化您的 const 变量。
vec
是移动的,不是复制的。
我需要一种 "elegant" 方法来在声明阶段使用另一个向量的内容和一些额外元素来初始化一个向量。
我要解决的是:
让我们考虑以下带有初始化的(示例)声明:
const std::vector<std::string> c90_types = {
"char",
"signed char",
"unsigned char",
"short",
"unsigned short",
"int",
"unsigned int",
"long",
"unsigned long",
"float",
"double",
"long double"
};
const std::vector<std::string> c99_types = {
"char",
"signed char",
"unsigned char",
"short",
"unsigned short",
"int",
"unsigned int",
"long",
"unsigned long",
"float",
"double",
"long double",
"long long",
"unsigned long long",
"intmax_t",
"uintmax_t"
};
如您所见,c99_types
有一个子集恰好是 c90_types
。我想避免需要更改子集然后手动更改 "superset" 的情况,只是为了避免可能引入错误的额外步骤:)
附带说明一下,我不想编写如下代码:
second.insert(second.begin(), first.begin(), first.end());
second.push_back(something);
对此有什么好的、干净的解决方案吗?
使用额外的代码,您可能仍然有 const 向量:
std::vector<std::string> make_c99_type()
{
auto res = c90_types;
const std::vector<std::string> extra_c99_types = {
"long long",
"unsigned long long",
"intmax_t",
"uintmax_t"
};
res.insert(res.end(), extra_c99_types.begin(), extra_c99_types.end());
return res;
}
const std::vector<std::string> c99_types = make_c99_type();
你可以先定义最大的向量(这里是c99_types),然后用最大的向量构造其他向量
这是一个例子:
const vector<int> a{1,2,3,4};
const vector<int> b{begin(a), begin(a)+2}; // b is {1,2}
所以你可以这样写:
const std::vector<std::string> c99_types = {
"char",
"signed char",
"unsigned char",
"short",
"unsigned short",
"int",
"unsigned int",
"long",
"unsigned long",
"float",
"double",
"long double",
"long long",
"unsigned long long",
"intmax_t",
"uintmax_t"
};
const std::vector<std::string> c90_types{begin(c99_types), begin(c99_types)+12};
选项 1:std::array
这可能可以清理和改进很多,但它至少是一个起点(它使用 Jonathan Wakely's redi::index_tuple
:
template<typename T, std::size_t N, unsigned... I, typename ...U>
inline auto
append_array_helper(const std::array<T, N>& array, redi::index_tuple<I...>, U&&... elements) -> std::array<T, N + sizeof...(elements)>
{
return std::array<T, N + sizeof...(elements)>{ std::get<I>(array)..., std::forward<U>(elements)... };
}
template<typename T, std::size_t N, typename ...U>
inline auto
append_array(const std::array<T, N>& array, U&&... elements) -> std::array<T, N + sizeof...(elements)>
{
return append_array_helper(array, typename redi::make_index_tuple<N>::type(), std::forward<U>(elements)...);
}
const std::array<std::string, 12> c90_types = {
"char",
"signed char",
"unsigned char",
"short",
"unsigned short",
"int",
"unsigned int",
"long",
"unsigned long",
"float",
"double",
"long double"
};
const std::array<std::string, 16> c99_types = append_array(
c90_types,
"long long",
"unsigned long long",
"intmax_t",
"uintmax_t"
);
如果不想指定数组大小,可以使用以下方法:
template<typename T, typename... U>
constexpr auto make_array(U&&... elements) -> std::array<T, sizeof...(elements)>
{
return { std::forward<U>(elements)... };
}
const auto c90_types = make_array<std::string>(
"char",
"signed char",
"unsigned char",
"short",
"unsigned short",
"int",
"unsigned int",
"long",
"unsigned long",
"float",
"double",
"long double"
);
...
选项 2:宏
不是我最喜欢的,但它简单易懂,易于编辑:
#define C90_TYPES \
"char", \
"signed char", \
"unsigned char", \
"short", \
"unsigned short", \
"int", \
"unsigned int", \
"long", \
"unsigned long", \
"float", \
"double", \
"long double"
#define C99_TYPES \
C90_TYPES, \
"long long", \
"unsigned long long", \
"intmax_t", \
"uintmax_t"
const std::vector<std::string> c90_types = {
C90_TYPES
};
const std::vector<std::string> c99_types = {
C99_TYPES
};
您可以使用 boost::join
:
#include <vector>
#include <boost/range/join.hpp>
const std::vector<std::string> c90_types = {
"char",
"signed char",
"unsigned char",
"short",
"unsigned short",
"int",
"unsigned int",
"long",
"unsigned long",
"float",
"double",
"long double"
};
auto range = boost::join(c90_types, std::vector<std::string>{
"long long",
"unsigned long long",
"intmax_t",
"uintmax_t"
});
const std::vector<std::string> c99_types(range.begin(), range.end());
有一个叫做 "I want to initialize a const variable with something elaborate." 的技巧在 C++11 中成为可能,无耻地从 Javascript 中偷走了。
const std::vector<std::string> c90_types = {
"char",
// and so on, and so forth....
};
const std::vector<std::string> c99_types = ([&](){
const auto additional_types = { // initializer_list<const char *>, but it does not matter.
"long long",
"unsigned long long",
"intmax_t",
"uintmax_t"
};
std::vector<std::string> vec{c90_types};
vec.insert(vec.end(), additional_types.begin(), additional_types.end());
return vec;
})();
将您的初始化逻辑打包到一个未命名的 lambda 中,并立即调用它,复制初始化您的 const 变量。
vec
是移动的,不是复制的。