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

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并获得类似的错误.

所以我的问题:

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

Cas*_*sey 5

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)