gsh*_*hep 2 c++ templates stl c++11 visual-studio-2013
我想使用新的C++ 11特性'extern模板类'与可移动对象的STL容器(不可复制)并获得编译器错误.
例:
MyFile.hpp
#pragma once
#include <cstdio>
class MyFile
{
std::FILE * handle;
public:
MyFile(const char * filename);
~MyFile();
MyFile(MyFile && that);
MyFile & operator=(MyFile && that);
MyFile(const MyFile&) = delete;
void operator=(const MyFile&) = delete;
std::FILE const * getFile() const;
};
Run Code Online (Sandbox Code Playgroud)
MyFile.cpp
:
#include "MyFile.hpp"
#include <iostream>
MyFile::MyFile(const char * filename)
: handle{nullptr}
{
if (!(handle = fopen(filename, "r")))
throw std::runtime_error("blah blah blah");
}
MyFile::~MyFile()
{
std::cout << "File::~File()" << std::endl;
if (handle)
fclose(handle);
}
MyFile::MyFile(MyFile && that)
: handle{nullptr}
{
*this = std::move(that);
}
MyFile & MyFile::operator =(MyFile && that)
{
std::swap(handle, that.handle);
return *this;
}
const std::FILE * MyFile::getFile() const
{
return handle;
}
Run Code Online (Sandbox Code Playgroud)
FileDeque.hpp
:
#pragma once
#include <deque>
#include "MyFile.hpp"
extern template class std::deque<MyFile>;
using FileDeque = std::deque<MyFile>;
Run Code Online (Sandbox Code Playgroud)
FileDeque.cpp
:
#include "FileDeque.hpp"
template class std::deque<MyFile>;
Run Code Online (Sandbox Code Playgroud)
测试程序:#include
using namespace std;
#include "MyFile.hpp"
#include "FileDeque.hpp"
int main()
{
cout << "Hello World!" << endl;
{
FileDeque files;
files.emplace_back("C:/eula.1028.txt");
files.emplace_back("C:/eula.1031.txt");
files.emplace_back("C:/eula.2052.txt");
}
return 0;
}
Run Code Online (Sandbox Code Playgroud)
使用Visual Studio 2013,我收到以下错误:
D:\ WinPrograms\Microsoft Visual Studio 12.0\VC\INCLUDE\deque(1714):错误C2280:'MyFile :: MyFile(const MyFile&)':尝试引用已删除的函数
d:\ devel\unique_ptr3\MyFile.hpp(18):查看'MyFile :: MyFile'的声明
D:\ WinPrograms\Microsoft Visual Studio 12.0\VC\INCLUDE\deque(1682):编译类模板成员函数'void std :: deque> :: _ Insert_n(std :: _ Deque_const_iterator >>,unsigned int,const MyFile&) '[_Ty = MyFile]
D:\ WinPrograms\Microsoft Visual Studio 12.0\VC\INCLUDE\deque(1510):参见函数模板实例化'void std :: deque> :: _ Insert_n(std :: _ Deque_const_iterator >>,unsigned int,const MyFile&)用[_Ty = MyFile]编译
d:\ devel\unique_ptr3\FileDeque.hpp(7):参见使用[_Ty = MyFile]编译的类模板实例化'std :: deque>'
生成代码......
很明显,编译器试图实例化使用对象复制的std :: deque> :: _ Insert_n函数,但为什么呢?
如果std :: deque直接在main.cpp中使用,我得到没有错误:
#include <iostream>
#include <deque>
using namespace std;
#include "MyFile.hpp"
using FileDeque = std::deque<MyFile>;
int main()
{
cout << "Hello World!" << endl;
{
FileDeque files;
files.emplace_back("C:/eula.1028.txt");
files.emplace_back("C:/eula.1031.txt");
files.emplace_back("C:/eula.2052.txt");
}
return 0;
}
Run Code Online (Sandbox Code Playgroud)
也尝试使用clang和gcc并获得类似的错误.
所以我的问题:
C++ 11 [temp.explicit]/8个状态:
命名类模板特化的显式实例化也是其每个成员(不包括从基类继承的成员)的相同类型(声明或定义)的显式实例化,该实例之前未明确专门化在包含该类的转换单元中.显式实例化,除非如下所述.
由于某些成员std::deque<foo>
需要一个可复制的类型foo
- 至少是复制构造函数 - 实例化它们是不正确的.这是您观察到的错误的原因.
解决方法是明确地仅实例化程序使用的格式良好的成员,例如:
// in FileDeque.hpp:
// Uncomment this to get linker errors suggesting
// other members to explicitly instantiate:
// extern template class std::deque<MyFile>;
extern template std::deque<MyFile>::deque();
extern template std::deque<MyFile>::~deque();
extern template auto std::deque<MyFile>::begin() -> iterator;
extern template auto std::deque<MyFile>::end() -> iterator;
// ...
// in FileDeque.cpp:
template std::deque<MyFile>::deque();
template std::deque<MyFile>::~deque();
template auto std::deque<MyFile>::begin() -> iterator;
template auto std::deque<MyFile>::end() -> iterator;
// ...
Run Code Online (Sandbox Code Playgroud)