问题背景

想要实现一个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下面的语句才会被编译,否则直接跳过。