AF_*_*cpp 26 c++ templates pimpl-idiom unique-ptr language-lawyer
为什么make_unique调用编译?make_unqiue不要求其模板参数是完整类型吗?
struct F;
int main()
{
std::make_unique<F>();
}
struct F {};
Run Code Online (Sandbox Code Playgroud)
在从orignated问题我的"问题"与我PIMPL实现:
我确实理解为什么析构函数必须在用户声明并在实现类(PIMPL)的cpp文件中定义.
但是移动包含pimpl的类的构造函数仍会编译.
class Object
{};
class CachedObjectFactory
{
public:
CachedObjectFactory();
~CachedObjectFactory();
std::shared_ptr<Object> create(int id) const;
private:
struct CacheImpl;
std::unique_ptr<CacheImpl> pImpl;
};
Run Code Online (Sandbox Code Playgroud)
现在cpp文件:
// constructor with make_unique on incompete type ?
CachedObjectFactory::CachedObjectFactory()
: pImpl(std::make_unique<CacheImpl>())
{}
struct CachedObjectFactory::CacheImpl
{
std::map<int, std::shared_ptr<Object>> idToObjects;
};
//deferred destructor
CachedObjectFactory::~CachedObjectFactory() = default;
Run Code Online (Sandbox Code Playgroud)
有人可以解释为什么这个编译?为什么建筑和破坏之间存在差异?如果析构函数的实例化和default_deleter的实例化是一个问题,为什么make_unique的实例化不是问题?
小智 16
make_unique有多个实例化点:翻译单元的结尾也是实例化的一个点.你看到的是编译器只实例化make_unique一次CacheImpl/ F完成.允许编译器执行此操作.如果您依赖它,您的代码就会格式不正确,并且编译器不需要检测错误.
14.6.4.1实例化点[temp.point]
8函数模板,成员函数模板或类模板的成员函数或静态数据成员的特化可以在翻译单元中具有多个实例化点,并且除了上述实例化的点之外,对于任何在翻译单元内具有实例化点的这种专门化,翻译单元的结尾也被认为是实例化的点.[...]如果两个不同的实例化点根据一个定义规则(3.2)给出了模板特化的不同含义,则该程序是不正确的,不需要诊断.
Rei*_*ica 13
这个编译的原因在于[temp.point]8:
函数模板,成员函数模板或类模板的成员函数或静态数据成员的特化可以在翻译单元内具有多个实例化点,并且除了上述实例化的点之外,对于任何这样的实例化.在翻译单元内具有实例化点的专门化,翻译单元的末尾也被认为是实例化的点.类模板的特化在翻译单元中最多只有一个实例化点[...]如果两个不同的实例化点根据一个定义规则给模板专门化赋予不同的含义,那么程序就是格式错误,没有需要诊断.
请注意这个引用的结尾,因为我们将在下面的编辑中看到它,但是现在根据OP的片段在实践中发生的是编译器使用另外考虑的实例化点make_unique()放在翻译单元的末尾,因此它将具有在代码中的原始使用点处缺少的定义.根据规范中的该条款允许这样做.
请注意,这不再编译:
struct Foo; int main(){ std::make_unique<Foo>(); } struct Foo { ~Foo() = delete; };
Run Code Online (Sandbox Code Playgroud)
同样,编译器不会错过实例化,它只会根据它用于生成模板代码的转换单元中的哪个点来推迟它.
编辑:最后似乎即使您有这些多个实例化点,但这并不意味着如果这些点之间的定义不同,则定义行为.注意上面引用中的最后一句,根据该定义,该差异由一个定义规则定义.这可以直接从我对@hvd的回答中得到答案,他在这里引用了这一点:在One Definition Rule中看到:
每个程序应该只包含每个非内联函数或变量的一个定义,该函数或变量在废弃语句之外的程序中使用.无需诊断.该定义可以在程序中明确显示,可以在标准或用户定义的库中找到,或者......
因此在OP的情况下,这显然是两个实例化点之间的差异,因为正如@hvd自己指出的那样,第一个是不完整类型,第二个不是.实际上,这种差异构成了两种不同的定义,因此毫无疑问这个程序是不正确的.
| 归档时间: |
|
| 查看次数: |
1134 次 |
| 最近记录: |