-1

问题很简单,一般来说,静态演员表(或其他演员表)是否安全

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

不担心这会沉别人的船吗?还是一般来说这是不安全的?

4

1 回答 1

2

暂时忽略你说的 std::vector 并假装你有其他一些不太明确的向量实现。您的代码在技术上是不安全的T,不是因为T 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,我对标准库规范的细节不够熟悉,无法知道这些专业化是否符合要求。也许更精通标准的人可以发表评论。

请注意我在回答将模板类转换为更一般的专业化时所说的一些内容可能有助于解释这个问题。

为了解决您关于检测特化的问题,有一些方法可以通过不使用类的特化但重载非成员函数来使您的代码不安全,我不确定您将如何检测到这一点。比如下面的:

#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/453436什么是 C++ 中的代理类

于 2013-01-09T04:50:34.687 回答