我有一个如下所示的头文件 -
// abc.hpp
#include <vector>
#include <string>
namespace A
{
namespace B
{
struct abc
{
std::string _type;
};
using abc_vector = std::vector<abc>;
}
}
我在另一个头文件中使用前向声明。
// con.hpp
#include <vector>
namespace A
{
namespace B
{
struct abc; // Forward Declaration
using abc_vector = std::vector<abc>;
}
namespace C
{
class N
{
public:
B::abc_vector foo(std::string type);
};
}
}
让我感到困惑的是我的代码编译和工作。
如何使用不完整类型声明向量...?
我认为它不应该能够决定
using abc_vector = std::vector<abc>;
以下是我用来测试头文件的代码。 奇怪的是,它编译并且工作得很好。
#include "con.hpp"
#include "abc.hpp"
#include <iostream>
namespace A
{
namespace C
{
B::abc_vector N::foo(std::string type)
{
B::abc a;
a._type = type;
B::abc_vector d;
d.push_back(a);
return d;
}
}
}
int main()
{
A::C::N n;
auto container = n.foo("test");
for (const auto& i : container)
std::cout << i._type << ' ';
return 0;
}
答案 0 :(得分:3)
代码行
using abc_vector = std::vector<abc>;
仅为std::vector<abc>
引入了类型别名。无论如何,这都不需要abc
的大小,因为根本没有分配abc
类型的对象。只声明了一种新类型。
B::abc_vector d;
确实需要abc
的定义。不过它可行,因为此时abc
已经定义,因为已包含头文件abc.hpp
。
您指的是this answer,其中
std::vector<B> v;
已“完成”。这跟你的做法不一样。您刚刚介绍了一个类型别名。 std::vector<B> v;
实际上定义了变量。因此,B
的定义是强制性的。
请注意
using abc_vector = std::vector<abc>;
相当于
typedef std::vector<abc> abc_vector;
也许这使得为什么在编译的这个时间点不需要知道abc
的大小更清楚一点。
答案 1 :(得分:2)
这是一个有趣的话题(至少对我而言)并适用于其他std容器。
最初,标准使得实例化不完整类型的容器成为未定义的行为。但是实现并没有禁止它。这很可能不是故意的,而只是因为(例如向量)中的元素存储在由指针引用的内存位置这一事实的副作用。
因此,在实际需要一个元素之前,不需要知道元素的大小 - 在向量的成员函数的实例化期间。
如果您想进一步探索,这是研究的起点:
http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2014/n4056.html
答案 2 :(得分:2)
有一个有趣的观察结果。 GCC5.2
和CLANG3.6
都编译以下代码。
struct A;
std::vector<A> my_func(); //Definition of my_func is in some CPP file
但是为
抛出错误struct A;
std::vector<A> v;
对此的推理是,矢量的大小不会因为它所持有的不同类型而发生变化。请参阅以下代码段。
struct B{int i; int j;};
struct C{int a,b,c;};
std::vector<B> pp;
std::vector<C> qq;
int main()
{
std::cout<<sizeof(pp)<<'\n';
std::cout<<sizeof(qq)<<'\n';
}
输出
24
24
但对于std::vector<A> v
,它也必须提供Allocator<A>()
。而分配器需要struct A
的成员,如构造函数,复制构造函数,析构函数等。
此处需要注意的一件重要事情是pointer arithmetic for incomplete type is not allowed.
如果您看到CLANG抛出的错误,它显然会说同样的错误。
In file included from /tmp/gcc-explorer-compiler115920-68-1xsb8x7/example.cpp:2:
In file included from /usr/bin/../lib/gcc/x86_64-linux-gnu/4.9/../../../../include/c++/4.9/vector:64:
/usr/bin/../lib/gcc/x86_64-linux-gnu/4.9/../../../../include/c++/4.9/bits/stl_vector.h:161:9: error: arithmetic on a pointer to an incomplete type 'A'
- this->_M_impl._M_start); }
^
/usr/bin/../lib/gcc/x86_64-linux-gnu/4.9/../../../../include/c++/4.9/bits/stl_vector.h:253:7: note: in instantiation of member function 'std::_Vector_base<A, std::allocator<A> >::~_Vector_base' requested here
vector()
^
其他一切都很直接。
以下是typedef,因此编译器需要了解大小。
using abc_vector = std::vector<abc>;
因此,讨论的代码结构很适合继续。