构建相同参数类型但具有不同常量限定符的std :: vector

时间:2013-01-09 03:46:58

标签: c++ const stdvector static-cast

这个问题非常简单,它通常是安全的静态演员(或其他演员)来自

std::vector< Foo >

std::vector< const Foo >

二进制方式,我不明白为什么本机类型会有所不同,毕竟const是一个不应该影响元素大小的语言约束,或者我认为

我能做到吗

std::vector< const Foo >& someFunc()
{
  std::vector< Foo >& ref = ...
  return *reinterpret_cast<std::vector< const Foo >*>(& ref);
}

并不担心这会沉没某人的船吗?或者这一般是不安全的吗?

1 个答案:

答案 0 :(得分:2)

忽略你刚才说std :: vector并且假装你有一些其他不太明确的矢量实现。您的代码在技术上不安全,不是因为TT const完全不同,而是因为C ++语言允许vector<T>vector<T const>专门用于完全不同的方式。请考虑以下代码:

#include <iostream>

template <class T>
struct vector {
    T* start_;
    T* end_;
    T* cap_end_;
};

template <class T>
struct vector<T const> {
    bool gotcha_;
    T* start_;
    T* end_;
    T* cap_end_;
};

struct foo { };

int
main()
{
    std::cout
        << sizeof(vector<foo>) << '\n'
        << sizeof(vector<foo const>) << '\n'
        ;
}

请注意,还有其他更有害的变化可能会让您的生活变得悲惨。例如以下成员重新排序的地方:

#include <iostream>

template <class T>
struct vector {
    T* start_;
    T* end_;
    T* cap_end_;
};

template <class T>
struct vector<T const> {
    T* end_;
    T* cap_end_;
    T* start_;
};

template <class T>
long size(vector<T> const& v)
{
    return v.end_ - v.start_;
}

struct foo { };

int
main()
{
    vector<foo> v;
    v.start_ = new foo[10];
    v.end_ = v.start_ + 1;
    v.cap_end_ = v.start_ + 10;


    std::cout
        << size(v) << '\n'
        << size(*reinterpret_cast<vector<foo const>*>(&v)) << '\n'
        ;

    return 0;
}

Wrt to std :: vector,我不熟悉标准库规范的细节,以了解这些特化是否符合要求。也许更精通标准的人可以评论。

请注意我在回答Casting templated class to more general specialization时所说的一些内容可能有助于解释这个问题。

要解决有关检测特化问题的问题,可以通过不使用类的特殊化但重载非成员函数来使代码不安全,我不知道如何检测它。如下:

#include <iostream>

template <class T>
struct vector {
    T* start_;
    T* end_;
    T* cap_end_;
};


template <class T>
void init(vector<T>& v, size_t sz, size_t cap)
{
    v.start_ = new T[cap];
    v.end_ = v.start_ + sz;
    v.cap_end_ = v.start_ + cap;
}

template <class T>
void init(vector<T const>& v, size_t sz, size_t cap)
{
    v.end_ = new T const[cap];
    v.cap_end_ = v.end_ + sz;
    v.start_ = v.end_ + cap;
}

template <class T>
long size(vector<T>& v)
{
    return v.end_ - v.start_;
}

template <class T>
long size(vector<T const>& v)
{
    return v.cap_end_ - v.end_;
}

struct foo { };

int
main()
{
    vector<foo const> v;
    init(v, 1, 10);

    std::cout
        << size(v) << '\n'
        << size(*reinterpret_cast<vector<foo>*>(&v)) << '\n'
        ;
}

足够的坏消息。好消息是,如果您想要使用通用接口获取现有对象并限制或调整可以对该对象执行的操作,则可以使用一些简单,安全且易于理解的方法。看一下std :: stack http://www.sgi.com/tech/stl/stack.html或者这个答案https://stackoverflow.com/a/994925/453436What is Proxy Class in C++