【问题标题】:Marking function as virtual causes compiler error with unique_ptr将函数标记为虚拟会导致编译器错误,unique_ptr
【发布时间】:2013-01-11 08:01:17
【问题描述】:

我有一个包装矢量的模板类。我正在尝试将 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;
}

【问题讨论】:

  • 顺便说一句,你的整个构造函数是不必要的。

标签: c++ templates c++11 instantiation overload-resolution


【解决方案1】:

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

你可以看看这个帖子:When the virtual member functions of a template class instantiated?

【讨论】:

  • 哇,+1,不知道。
【解决方案2】:

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

Standard14.7.1 隐式实例化 [temp.inst] 中有 2 个相关引号:

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

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

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

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

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

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

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-10-30
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多