外部模板类std::可移动对象的容器

extern template class std::container of movable objects

本文关键字:对象 可移动 外部 std      更新时间:2023-10-16

我想使用新的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;
};

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;
}

FileDeque.hpp:

#pragma once
#include <deque>
#include "MyFile.hpp"
extern template class std::deque<MyFile>;
using FileDeque = std::deque<MyFile>;

FileDeque.cpp:

#include "FileDeque.hpp"
template class std::deque<MyFile>;

,测试程序:# 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;
}

使用Visual Studio 2013,我得到以下错误:

D:WinProgramsMicrosoft Visual Studio 12.0VCINCLUDEdeque(1714):错误C2280: 'MyFile::MyFile(const MyFile &)':试图引用已删除的函数

d:develunique_ptr3MyFile.hpp(18):查看声明'MyFile::MyFile'

D:WinProgramsMicrosoft Visual Studio 12.0VCINCLUDEdeque(1682):在编译类模板成员函数'void std::deque>::_Insert_n(std::_Deque_const_iterator>>,unsigned int,const MyFile &)'时与[_Ty = MyFile)

D:WinProgramsMicrosoft Visual Studio 12.0VCINCLUDEdeque(1510):参考函数模板实例化'void std::deque>::_Insert_n(std::_Deque_const_iterator>>,unsigned int,const MyFile &)'正在编译与[_Ty = MyFile)

d:develunique_ptr3 filedequeue .hpp(7):参见编译类模板实例化` std::deque> `的参考与[_Ty = MyFile)

生成代码…

很明显,编译器试图实例化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;
}

也尝试了clang和gcc,得到类似的错误。

我的问题是:

  1. 是否有可能使编译器不实例化容器的可移动对象类?为什么编译器试图实例化需要复制支持的方法?
  2. 我想错了吗?

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;
// ...