vector<char *>
类型无法转换为const vector<const char*>
。例如,以下内容给出了编译错误:
#include <vector>
using namespace std;
void fn(const vector<const char*> cvcc)
{
}
int main()
{
vector<char *> vc = vector<char *>();
fn(vc);
}
我理解为什么vector<char*>
无法转换为vector<const char*>
- 类型const char *
的额外成员可能会被添加到向量中,之后它们可以作为非const访问。但是,如果向量本身是常量,则不会发生这种情况。
我最好的猜测是,这将是无害的,但是编译器无法推断出这将是无害的。
如何解决这个问题?
这个问题是由C ++ FQA here提出的。
答案 0 :(得分:11)
通常,C ++不允许您将someclass<T>
投射到someclass<U>
,因为模板someclass
可能专门用于U
。 T
和U
之间的关系并不重要。这意味着实现以及对象布局可能不同。
可悲的是,没有办法告诉编译器在哪些情况下布局和/或行为没有改变并且应该接受演员表。我想这对std::shared_ptr
和其他用例非常有用,但这不会很快发生(AFAIK)。
答案 1 :(得分:6)
void fn(const vector<const char*>)
当函数类型的顶级const
限定符被删除时,这是(在调用站点)相当于:
void fn(vector<const char*>)
两者都请求传递的向量的副本,因为标准库容器遵循值语义。
你可以:
fn({vc.begin(), vc.end()})
调用它,请求显式转换void fn(vector<const char*> const&)
,即参考如果您可以修改fn
的签名,则可以关注GManNickG的advice and use iterators / a范围:
#include <iostream>
template<typename ConstRaIt>
void fn(ConstRaIt begin, ConstRaIt end)
{
for(; begin != end; ++begin)
{
std::cout << *begin << std::endl;
}
}
#include <vector>
int main()
{
char arr[] = "hello world";
std::vector<char *> vc;
for(char& c : arr) vc.push_back(&c);
fn(begin(vc), end(vc));
}
这给出了漂亮的输出
hello world ello world llo world lo world o world world world orld rld ld d
根本问题是传递标准库容器。如果您只需要持续访问数据,则无需知道实际的容器类型,而是可以使用模板。这将删除fn
与调用者使用的容器类型的耦合。
正如您所注意到的,允许通过std::vector<T*>
访问std::vector<const T*>&
是一个坏主意。但是,如果您不需要修改容器,则可以改为使用范围。
如果函数fn
不能或不能作为模板,您仍然可以传递范围const char*
而不是const char
的向量。这适用于任何保证连续存储的容器,例如原始数组,std::array
,std::vector
和std::string
。
答案 2 :(得分:1)
要解决此问题,您可以实现自己的模板包装器:
template <typename T> class const_ptr_vector;
template <typename T> class const_ptr_vector<T *> {
const std::vector<T *> &v_;
public:
const_ptr_vector (const std::vector<T *> &v) : v_(v) {}
typedef const T * value_type;
//...
value_type operator [] (int i) const { return v_[i]; }
//...
operator std::vector<const T *> () const {
return std::vector<const T *>(v_.begin(), v_.end());
}
//...
};
我们的想法是包装器提供了引用向量的接口,但返回值始终为const T *
。由于引用的向量是const
,因此包装器提供的所有接口也应该是const
,如[]
运算符所示。这包括由包装器提供的迭代器。包装器迭代器只包含引用向量的迭代器,但产生指针值的所有操作都是const T *
。
此变通方法的目标不是提供可以传递给想要const std::vector<const T *> &
的函数的东西,而是提供用于提供此类向量的类型安全性的函数的不同类型。 / p>
虽然您无法将此传递给期望const std::vector<const T *> &
的函数,但您可以实现一个转换运算符,该运算符将返回使用基础向量中的指针值初始化的std::vector<const T *>
的副本。上面的例子也说明了这一点。
答案 3 :(得分:-3)
正如FQA所暗示的,这是C ++的一个根本缺陷。
看来你可以通过一些明确的演员来做你想做的事情:
vector<char*> vc = vector<char *>();
vector<const char*>* vcc = reinterpret_cast<vector<const char*>*>(&vc);
fn(*vcc);
这会调用未定义的行为,但不保证可以正常工作;但是,我几乎可以肯定它会在关闭严格别名(-fno-strict-aliasing
)的gcc中工作。无论如何,这只能作为临时黑客行事;你应该只是复制矢量,以保证的方式做你想做的事。
std::copy(vc.begin(), vc.end(), std::back_inserter(vcc));
从性能角度来看,这也没关系,因为fn
在调用时会复制其参数。