7

我有一个包装矢量的模板类。我正在尝试将 unique_ptrs 存储在此类中,并且效果很好。但是,当我将该void add(const T& elem)函数标记为虚拟时,我的编译器 (clang) 告诉我,我正在为 unique_ptr 进行“调用隐式删除的复制构造函数”。

我知道 unique_ptrs 不能被复制,所以这就是我创建该void add(T&& elem)函数的原因。我只是不知道为什么将另一个添加函数标记为虚拟会导致编译器错误。

谢谢你的时间。

#include <iostream>
#include <vector>
#include <memory>

using namespace std;

template <typename T>
class ContainerWrapper {
private:
    vector<T> vec;
public:
    ContainerWrapper() : vec() {

    }

    //Marking this as virtual causes a compiler error
    void add(const T& elem) {
        vec.push_back(elem);
    }

    void add(T&& elem) {
        vec.push_back(std::move(elem));
    }

    T removeLast() {
        T last = std::move(vec.back());
        vec.pop_back();
        return last;
    }
};

int main() {
    ContainerWrapper<unique_ptr<string>> w;
    w.add(unique_ptr<string>(new string("hello")));

    unique_ptr<string> s = w.removeLast();

    cout << *s << endl;
}
4

2 回答 2

9

这很可能是因为 ContainerWrapper 是一个模板。使用模板,只要您不调用它们,编译器通常甚至不会检查成员函数。但是,将其标记为虚拟会强制该功能存在(您甚至可能会遇到链接错误)。

您可以看看这篇文章:模板类的虚拟成员函数何时实例化?.

于 2013-01-28T01:41:24.337 回答
5

这里的问题是std::unique_ptr它的复制构造函数标记为=delete. 这意味着重载成员函数vec.push_back(elem)内部的调用在使用. 一旦该成员函数被实例化,编译器就会意识到这一点。add(T const&)std::unique_ptr

标准在14.7.1 隐式实例化 [temp.inst]中有 2 个相关引用:

6 如果重载决策过程可以在不实例化类模板定义的情况下确定要调用的正确函数,则未指定实例化是否实际发生。

10 [...] 如果虚拟成员函数不会被实例化,则未指定实现是否隐式实例化类模板的虚拟成员函数。[...]

第 6 条规定 - 没有virtual关键字 - 编译器被允许但不需要实例化两者add(T const&),并且add(T&&)为了解决哪个重载是最佳匹配。gcc 4.7.2 和 Clang 3.2 都不需要实例化,因为它们碰巧推断出右值引用总是比左值引用更适合临时对象。

第 10 条规定 - 即使使用virtual关键字 - 编译器也被允许但不需要实例化add(T const&)add(T&&)以便确定哪个重载是最佳匹配。gcc 4.7.2 和 Clang 3.2 都实例化了这两个成员函数,尽管它们都可以推断出左值重载永远不会是更好的匹配。

请注意,如果您ContainerWrapper使用嵌套的常规类typedef unique_ptr<string> T;,则 gcc 和 Clang 都会生成带有或不带有virtual关键字的错误,因为它们必须为两个成员函数生成代码。这不会被 SFINAE 删除,因为在替换推导参数期间不会发生错误。

结论:这不是错误,而是实施质量问题。

于 2013-01-28T12:41:52.693 回答