Mic*_*Łoś 15 c++ templates language-lawyer c++11 c++14
码:
#include <memory>
struct Data;
std::unique_ptr<Data> make_me();
int main()
{
std::unique_ptr<Data> m = make_me();
return 0;
}
Run Code Online (Sandbox Code Playgroud)
当然失败了:
In file included from <source>:1:
In file included from /opt/compiler-explorer/gcc-7.1.0/lib/gcc/x86_64-linux-gnu/7.1.0/../../../../include/c++/7.1.0/memory:80:
/opt/compiler-explorer/gcc-7.1.0/include/c++/7.1.0/bits/unique_ptr.h:76:16: error: invalid application of 'sizeof' to an incomplete type 'Data'
static_assert(sizeof(_Tp)>0,
^~~~~~~~~~~
/opt/compiler-explorer/gcc-7.1.0/include/c++/7.1.0/bits/unique_ptr.h:268:4: note: in instantiation of member function 'std::default_delete<Data>::operator()' requested here
get_deleter()(__ptr);
^
8 : <source>:8:31: note: in instantiation of member function 'std::unique_ptr<Data, std::default_delete<Data> >::~unique_ptr' requested here
std::unique_ptr<Data> m = make_me();
^
3 : <source>:3:8: note: forward declaration of 'Data'
struct Data;
^
1 error generated.
Compiler returned: 1
Run Code Online (Sandbox Code Playgroud)
但是在上面的代码末尾添加下面的行编译很好:
struct Data {};
Run Code Online (Sandbox Code Playgroud)
我的问题是为什么这个代码在std :: unique_ptr实例化之后声明Data时编译并运行?看起来,这两种情况都应该以相同/类似的错误失败.
关于godbolt的整个例子:https://godbolt.org/g/FQqxwN
Vit*_*meo 11
这是有效的,因为a 的实例化点template位于定义之后Data.从标准:
对于类模板特化,类成员模板特化或类模板的类成员的特化,如果特化是隐式实例化的,因为它是从另一个模板特化中引用的,如果引用特化的上下文取决于在模板参数上,如果在封闭模板的实例化之前未实例化特化,则实例化的点紧接在封闭模板的实例化之前.否则,这种特化的实例化点紧接在引用特化的命名空间范围声明或定义之前.
函数模板,成员函数模板或类模板的成员函数或静态数据成员的特化可以在翻译单元内具有多个实例化点,并且除了上述实例化的点之外,对于任何这样的实例化.在翻译单元内具有实例化点的专门化,翻译单元的末尾也被认为是实例化的点.类模板的专门化在翻译单元中最多只有一个实例化点.任何模板的特化可以在多个翻译单元中具有实例化点.如果两个不同的实例化点根据单定义规则给出模板特化的不同含义,则程序形成错误,不需要诊断.
请注意,由于引号中的最后一句,这可能是格式错误(NDR).我没有足够的信心判断这是否确实是不正确的.
如果你仔细阅读,问题是删除包含的Data对象.该
Run Code Online (Sandbox Code Playgroud)get_deleter()(__ptr)
部分是大提示.
这里发生的是,唯一指针对象m在main函数末尾超出范围,因此需要删除指向的数据.但是,由于没有析构函数,默认删除器无法处理它.
要解决它,您可以添加结构的定义,这将定义它,默认删除器将能够知道类型.或者你可以为指针添加一个新的删除器,其中一个(在这种情况下)可能无效:
auto null_deleter = [](Data*){ /* Do nothing */ };
...
std::unique_ptr<Data, decltype(null_deleter)> m = make_me();
Run Code Online (Sandbox Code Playgroud)
当然,如果你想要实际删除数据,那么要么定义结构,要么修改删除器使它delete成为指针(这使得它无论如何都需要完整的结构定义,但是删除器可以在另一个tranaslation-unit中定义,可能同样在哪里make_me定义).