将函数标记为虚拟会导致编译器错误与unique_ptr

Tor*_*ial 7 c++ templates instantiation overload-resolution c++11

我有一个包含矢量的模板类.我试图在这个类中存储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;
}
Run Code Online (Sandbox Code Playgroud)

Fra*_*san 9

这很可能是由于ContainerWrapper是一个模板.使用模板,只要您不调用它们,编译器通常甚至不会检查成员函数.但是,将其标记为虚拟会强制函数出现(您甚至可能会收到链接错误).

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


Tem*_*Rex 5

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

标准在这里有2个相关报价在14.7.1隐式实例[temp.inst] :

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

10 [...]如果虚拟成员函数不会被实例化,那么实现是否隐式实例化类模板的虚拟成员函数是不明确的.[...]

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

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

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

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