为什么指针向量不能转换为 const 指针的 const 向量?

2022-01-24 00:00:00 constants casting c++

The type vector<char *> is not convertible to const vector<const char*>. For example, the following gives a compilation error:

#include <vector>

using namespace std;

void fn(const vector<const char*> cvcc)
{
}

int main()
{
    vector<char *> vc = vector<char *>(); 

    fn(vc);
}

I understand why vector<char*> is not convertable to vector<const char*> - extra members of type const char * may be added to the vector, and afterwards they would be accessible as non-const. However, if the vector itself is const, this can't happen.

My best guess is that this would be harmless, but there is no way the compiler is allowed to deduce that this would be harmless.

How can this be worked around?

This question was suggested by the C++ FQA here.

解决方案

void fn(const vector<const char*>)

As the top-level const qualifier is dropped for the function type, this is (at the call site) equivalent to:

void fn(vector<const char*>)

Both of which request a copy of the passed vector, because Standard Library containers follow value semantics.

You can either:

  • call it via fn({vc.begin(), vc.end()}), requesting an explicit conversion
  • change the signature to, e.g. void fn(vector<const char*> const&), i.e. taking a reference

If you can modify the signature of fn, you can follow GManNickG's advice and use iterators / a range instead:

#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));
}

This gives the beautiful output

hello world
ello world
llo world
lo world
o world
 world
world
orld
rld
ld
d


The fundamental issue is to pass around Standard Library containers. If you only need constant access to the data, you don't need to know the actual container type and can use the template instead. This removes the coupling of fn to the type of container the caller uses.

As you have noticed, it's a bad idea to allow access of a std::vector<T*> through a std::vector<const T*>&. But if you don't need to modify the container, you can use a range instead.

If the function fn shall not or cannot be a template, you could still pass around ranges of const char* instead of vectors of const char. This will work with any container that guarantees contiguous storage, such as raw arrays, std::arrays, std::vectors and std::strings.

相关文章