【问题标题】:extern template class std::container of movable objects可移动对象的外部模板类 std::container
【发布时间】:2014-04-18 08:50:01
【问题描述】:

我想将新的 C++11 功能“外部模板类”与可移动对象的 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>;

还有测试程序: #包括

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 &)' : 试图引用已删除的函数

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我的文件 &)' 和 [ _Ty=我的文件 ]

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=我的文件 ]

d:\devel\unique_ptr3\FileDeque.hpp(7) : 请参阅对正在编译的类模板实例化 'std::deque>' 的引用 和 [ _Ty=我的文件 ]

生成代码...

很明显,编译器试图实例化使用对象复制的 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. 我想错了吗?

【问题讨论】:

  • 这不是外部模板的问题,而是显式实例化的问题。尝试将字符串 template class std::deque&lt;MyFile&gt;; 添加到 main 函数的第二个变体中。事实上,我认为内部函数_Insert_n 使用了复制构造函数。它不会出现在第二种情况下,因为隐式实例化不会实例化所有类模板成员(仅实例化那些真正使用的成员)。
  • 类模板的显式实例化实例化所有成员,无论格式是否正确。您必须仅显式实例化为您的参数格式正确的成员。
  • @Constructor:这就是问题所在,即我不希望 std::deque 在不同的翻译单元中多次实例化。
  • @Casey 是标准的要求还是实现的错误?您能否提供标准的摘录。
  • 这不是错误。有关详细信息,请参阅第 [temp.explicit] 14.7.2/8 和 14.7.2/9 段。

标签: c++ templates c++11 stl visual-studio-2013


【解决方案1】:

C++11 [temp.explicit]/8 个状态:

命名类模板特化的显式实例化也是其每个成员(不包括从基类继承的成员)的同种(声明或定义)的显式实例化,这些成员以前没有在翻译中显式特化包含显式实例化的单元,但下面描述的除外。

由于std::deque&lt;foo&gt; 的某些成员需要可复制类型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;
// ...

【讨论】:

  • 我希望我能给你另一个支持 “取消注释以获取链接器错误,建议其他成员显式实例化:”
猜你喜欢
  • 2020-07-30
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2014-06-11
  • 1970-01-01
相关资源
最近更新 更多