问题背景
想要实现一个append(C& contianer, T* ptr, size_t size)
函数,作用是拷贝size个数组ptr中的元素到容器container中。为了防止container扩容引发的性能开销,希望如果container有reserve成员,先调用reserve分配好内存,如果没有就算了。
template <typename C, typename T>
void append(C& container, T* ptr, size_t size)
{
container.reserve(container.size() + size); // 有reserve的版本
for (size_t i = 0; i < size; ++i) {
container.push_back(ptr[i]);
}
}
template <typename C, typename T>
void append(C& container, T* ptr, size_t size)
{
// 无reserve的版本
for (size_t i = 0; i < size; ++i) {
container.push_back(ptr[i]);
}
}
我们能不能像python一样,
template <typename C, typename T>
void append(C& container, T* ptr, size_t size)
{
if (has_reserve<C>::value) { // 实现一个trait,判断C有没有reserve成员
container.reserve(container.size() + size); // #1
}
for (size_t i = 0; i < size; ++i) {
container.push_back(ptr[i]);
}
}
很遗憾不行,因为当类型C没有reserve成员时,#1处直接编译错误。
解决方案
#include <vector>
#include <iostream>
#include <list>
#include <type_traits>
using namespace std;
template <typename T, typename = void_t<>>
struct has_reserve : false_type {};
template <typename T>
struct has_reserve<T, void_t<decltype(declval<T&>().reserve(1U))>> : true_type {};
template <typename C, typename T>
void append(C& container, T* ptr, size_t size)
{
if constexpr (has_reserve<C>::value) { // #2
container.reserve(container.size() + size);
}
for (size_t i = 0; i < size; ++i) {
container.push_back(ptr[i]);
}
}
template <typename T>
void print(const T& container)
{
cout << __PRETTY_FUNCTION__ << ": ";
for (auto it = container.begin(); it != container.end(); ++it) {
cout << *it << ' ';
}
cout << endl;
}
int main()
{
cout << has_reserve<vector<int>>::value << endl;
cout << has_reserve<list<int>>::value << endl;
int a[] = {1,2,3,4,5};
list<int> lst;
vector<int> vec;
// append(lst, data(a), std::size(a));
// append(vec, data(a), std::size(a));
append(lst, a, 5);
append(vec, a, 5);
print(lst); // void print(const T&) [with T = std::__cxx11::list<int>]: 1 2 3 4 5
print(vec); // void print(const T&) [with T = std::vector<int>]: 1 2 3 4 5
return 0;
}
这里#2处使用了if constexpr
功能,由c++17引入(被吴咏炜称为c++17最喜爱的特性)。他的作用是,直接在编译期判断条件,判过了,if下面的语句才会被编译,否则直接跳过。