3

背景:由于模板代码膨胀,STL 的使用使构建速度变慢:通常相同的方法在许多不同的翻译单元中独立实例化,并被多次编译和优化。避免模​​板类的这种对象代码重复的一种方法是使用显式模板实例化外部模板声明,但 STL 实现不支持它们。我正在尝试实现一个std::vector<T>支持显式实例化的等价物。


问题:我有一个模板类,如果模板参数不满足某些条件vector<T>,我希望删除它的一些方法。T更糟糕的是,这里有一些额外的要求:

  1. vector<T>无论是否T满足条件,都必须可以显式实例化。
  2. 当有人调用有条件删除的方法时,应该会发出编译错误。
  3. 我有几个这样的条件,以及一组依赖于每个条件的方法。

讨论:例如,该方法在不可复制vector<T>::push_back(const T&)时无法工作。T

如果我保持这个方法实现不变,那么如果我显式实例化 eg ,编译器将产生错误vector<unique_ptr<int>>,因为它将无法复制unique_ptr<int>. 这与要求 1 相矛盾,并使所需的代码膨胀优化变得不可能。

我可以对 进行两种不同的实现vector<T>::push_back(const T&),一种用于可复制构造的类型,另一种用于其他类型。这可以使用 SFINAE 重载或使用辅助模板类来完成。非复制构造案例的实现可以简单地抛出异常或调用终止。但是随后调用 for 的方法push_back只会vector<unique_ptr<int>>在运行时崩溃。如要求 2 所述,它不会产生编译错误。

最后,有几个条件,每个条件都排除了一些方法。该类型T可以在各种组合中缺少以下属性:可复制构造、可复制赋值、可默认构造、可移动构造。相关问题的解决方案似乎仅适用于一种条件,不满足要求 3。

我剩下的唯一想法是使用某种整体的部分专业化vector<T>与预处理器技巧相结合。但这需要像 16 个独立的专业,这听起来很糟糕。

PS很明显,STL 设计本质上依赖于隐式实例化的机制,这使得减少由它们引起的代码膨胀变得非常困难。

4

2 回答 2

3

你可以让重载决议std::conditional发挥它的魔力。这不会受到指数级专业化的影响,因为每种方法仅基于其自身的要求。

template<typename T>
class vector_errors
{
public:
    template<typename...>
    void push_back(const T&)
    {
        static_assert(std::is_copy_constructible_v<T>, "T must be copy constructible");
    }
};

class dummy
{
    dummy(const dummy&) {}  // disable construction
};

template<typename T>
class vector : vector_errors<T>
{
    using base = vector_errors<T>;
public:
    using base::push_back;
    void push_back(const std::conditional_t<std::is_copy_constructible_v<T>, T, dummy>& t)
    {
        if constexpr(std::is_copy_constructible_v<T>)
        {
            // do stuff
        }
    }
};

template class vector<int>;                   // instantiates push_back
template class vector<std::unique_ptr<int>>;  // error on push_back

居住

于 2018-02-24T14:36:40.403 回答
1

您可以使用 SFINAE。它完全按照您的要求工作:

struct X
{
    X() = default;
    X(const X&) = delete;
};


template <class T> struct V
{
    T* ptr_;


    template <class U = T, decltype(U{std::declval<U>()})* = nullptr>
    auto push_back(const T& val)
    {
        static_assert(std::is_same<U, T>::value, "explicit template parameter U provided.");
        // ... code
    }

    // you can have this or not, depending on your preferences
    // auto push_back(...) = delete;
};

// explicit instantiation OK
template struct V<int>;
// you then need to explicit instantiate all the method templates
template auto V<int>::push_back(const int&) -> void;

template struct V<X>; // explicit instantiation OK

// don't instantiate "disabled" methods
// template auto V<X>::push_back(const X&) -> void;


auto test()
{
    V<X> v;  // OK

    v.push_back(X{}); // compile time error
}

您可以显式实例化向量,可以创建对象,但在调用时会出现编译错误push_back

<source>:34:7: error: no matching member function for call to 'push_back'
    v.push_back(X{});
    ~~^~~~~~~~~
<source>:19:10: note: candidate template ignored: substitution failure [with U = X]: excess elements in struct initializer
    auto push_back(const U& val)
         ^

或者,如果您保留已删除的内容,push_back您将获得:

<source>:34:7: error: call to deleted member function 'push_back'
    v.push_back(X{});
    ~~^~~~~~~~~
<source>:26:10: note: candidate function has been explicitly deleted
    auto push_back(...) = delete;
         ^
<source>:19:10: note: candidate template ignored: substitution failure [with U = X]: excess elements in struct initializer
    auto push_back(const U& val)
         ^

唯一的麻烦是你需要显式地实例化所有的方法模板,但只有那些没有被禁用的。

于 2018-02-24T13:43:35.850 回答