我有一个包含矢量的模板类。我试图在这个类中存储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;
}
答案 0 :(得分:9)
这很可能是由于ContainerWrapper是一个模板。使用模板,只要您不打电话,编译器通常甚至不会检查成员函数。但是,将其标记为虚拟会强制函数出现(您甚至可能会出现链接错误)。
你可以看一下这篇文章:When the virtual member functions of a template class instantiated?。
答案 1 :(得分:5)
这里的问题是std::unique_ptr
的副本构造函数标记为=delete
。这意味着vec.push_back(elem)
重载成员函数内的add(T const&)
调用在使用std::unique_ptr
调用时将无法编译。一旦成员函数被实例化,编译器就会意识到这一点。
Standard在 14.7.1隐式实例化[temp.inst] 中有2个相关引号:
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,因为在替换推断的参数时不会发生错误。
结论:这是不是错误,而是实施质量问题。