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

时间:2014-03-13 14:26:52

标签: c++ templates c++11 stl 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;
};

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:\ WinPrograms \ Microsoft Visual Studio 12.0 \ VC \ INCLUDE \ deque(1714):错误C2280:'MyFile :: MyFile(const MyFile&amp;)':尝试引用已删除的函数

     

d:\ devel \ unique_ptr3 \ MyFile.hpp(18):参见'MyFile :: MyFile'的声明

     

D:\ WinPrograms \ Microsoft Visual Studio 12.0 \ VC \ INCLUDE \ deque(1682):编译类模板成员函数'void std :: deque&gt; :: _ Insert_n(std :: _ Deque_const_iterator&gt;&gt;,unsigned int, const MyFile&amp;)'           同           [               _Ty = MyFile的           ]

     

D:\ WinPrograms \ Microsoft Visual Studio 12.0 \ VC \ INCLUDE \ deque(1510):参见函数模板实例化'void std :: deque&gt; :: _ Insert_n(std :: _ Deque_const_iterator&gt;&gt ;, unsigned int, const MyFile&amp;)'正在编译中           同           [               _Ty = MyFile的           ]

     

d:\ devel \ unique_ptr3 \ FileDeque.hpp(7):请参阅类模板实例化'std :: deque&gt;'被编译           同           [               _Ty = MyFile的           ]

     

生成代码......

很明显,编译器试图实例化使用对象复制的std :: deque&gt; :: _ Insert_n函数,但为什么呢?

如果在main.cpp中直接使用std :: deque,我就不会出错:

#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. 我想要错吗?

1 个答案:

答案 0 :(得分: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;
// ...