一个选项是创建一个变长参数函数模板
make_vector
,检查当前参数是否支持
std::begin()
。如果它支持
std::begin()
,则使用
vector
的
insert
成员函数;否则,使用
emplace_back
。这样就可以从任何容器(具有正确的
T
)创建一个向量。
首先,一个类型特性用于检查当前参数是否支持std::begin()
,另一个类型特性则用于检查其是否支持std::size()
。 std::size()
将被用来计算最终vector
所需的空间大小。在这种情况下,为了避免在填充向量时重新分配空间(并移动元素),通常使用保留空间来确保已知最终(或最小)元素数量。
#include <iterator>
#include <type_traits>
template<typename T>
class has_begin ;
template<class T>
inline constexpr bool has_begin_v = has_begin<T>::value;
template<typename T>
class has_size {
template<typename TT>
static auto test(int) ->
decltype( std::size(std::declval<const TT&>()), std::true_type() );
template<typename>
static auto test(...) -> std::false_type;
public:
static constexpr bool value = decltype(test<T>(0))::value;
};
template<class T>
inline constexpr bool has_size_v = has_size<T>::value;
接下来是实际的make_vector
函数模板及其帮助程序,用于计算实际大小并确定如何处理一个特定的参数。
#include <utility>
#include <vector>
namespace detail {
template<class T, class Arg>
size_t make_vector_capacity(Arg&& arg) {
if constexpr(std::is_same_v<T, std::remove_cv_t<std::remove_reference_t<Arg>>>) {
return 1;
} else if constexpr(has_size_v<Arg>) {
return std::size(arg);
} else if constexpr(has_begin_v<Arg>) {
return std::distance(std::begin(arg), std::end(arg));
} else {
return 1;
}
}
template<class T, class Arg>
void make_vector_helper(std::vector<T>& v, Arg&& arg) {
if constexpr(std::is_same_v<T, std::remove_cv_t<std::remove_reference_t<Arg>>>) {
v.emplace_back(std::forward<Arg>(arg));
} else if constexpr(has_begin_v<Arg>) {
v.insert(v.end(), std::begin(arg), std::end(arg));
} else {
v.emplace_back(std::forward<Arg>(arg));
}
}
}
template<class T, class... Args>
std::vector<T> make_vector(Args&&... args) {
std::vector<T> rv;
rv.reserve( (detail::make_vector_capacity<T>(args) + ...) );
(detail::make_vector_helper(rv, std::forward<Args>(args)), ...);
return rv;
}
一个使用示例,混合不同的容器和单个 std::string
值:
#include <initializer_list>
#include <iostream>
#include <list>
#include <vector>
#include <string>
int main() {
using namespace std::string_literals;
const std::string arr[] = {"3"s, "4"s};
const std::vector vec{"5"s, "6"s, "7"s , "8"s};
const std::list list{"9"s,"10"s};
auto init = {"13"s, "14"s, "15"s};
const auto tmp2 = make_vector<std::string>("1"s, "2"s, arr, vec, list,
"11"s, "12"s, init );
for(const auto& e: tmp2) std::cout << e <<' ';
}
输出:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
演示
std::vector::insert
的内容,你可以想出类似于std::vector<int> tmp2{11, 99}; tmp2.insert(std::next(tmp2.begin()), tmp1.begin(), tmp1.end());
这样的代码。 - Ted Lyngmo