【问题标题】:Creating const objects of user class from const pointers从 const 指针创建用户类的 const 对象
【发布时间】:2016-02-10 23:03:39
【问题描述】:

是否可以强制编译器在我的类中传播 const 限定符,以便为构造函数提供 const 指针?考虑以下代码:

struct T
{
    T(int * a, int * b): 
        a(a), b(b) {}

    int * a;
    int * b;
};

int a = 1, b = 2;
const int * aRef = &a;
const int * bRef = &b;
const T obj(aRef, bRef); // error

这显然是不允许的,因为构造函数接受int *,而不是const int *。如果不打算修改 T 类的 const 对象中的 ab 的数据,有什么方法可以达到相同的效果?

编辑 这是一个稍微复杂一点的例子,它更接近实际问题。想象一下,我按顺序传递了一些int[] 的大型数组(例如,连续 1000 个整数),我想评估 k-th 位置的元素的运行最大值(即运行最大值也是 1000-整数向量)。当我将const int * 传递到传入数组的开头时,我设计了一个结构

struct ArrayWithMax
{
public:
    ArrayWithMax(int * array) : array(array) {}
    void Max(const ArrayWithMax& rhs);

private:
    int * array;
}

ArrayWithMax::Max 显然会循环遍历两个数组,并将最大值 (max(this->array[k], rhs.array[k]) 分配给对象的array。为简洁起见,我将跳过代码。

现在,我将存储结果的变量必须是非常量(因为 max-array 的元素会改变)。但是我收到的更新是const int *。对我来说最简单的方法是从 const int * 初始化 const ArrayWithMax,这正是问题试图实现的目标。

【问题讨论】:

  • 实际上这不是 int* 而是一个指向内存繁重的类的指针,我不想取消引用和复制,因此问题。
  • 那么非常量 T 会发生什么?
  • 我认为如果构造函数需要非常量数据,那么要么构造函数在你的控制之下,你可以修改它以接受 const 数据,或者它不是,然后假设 const数据就足够了是错误的,即使今天恰好是这样。您显然可以使用 const_cast 来消除 const 性,但那是 hack 的味道。
  • @LogicStuff 我没有看到使用非常量指针初始化非常量 T 对象有任何问题;不需要用 const 指针初始化非 const T 对象(而且确实不合逻辑)。
  • @enrico-granata 构造函数确实在我的控制之下。我无法修改它以接受 const 数据,因为我需要 T 类型的非常量对象具有非常量 ab。 const-cast 是一个选项,但考虑到我真的不想打破 const 规则,这是一个相当丑陋的选项。

标签: c++ pointers constants


【解决方案1】:

看来你想要的是:

const T obj(aRef, bRef);

导致您无法修改指针指向的整数的对象。这意味着成员指针为int const* constint const*

现实情况是,const 只是传播给成员,使它们都成为int* const,这意味着一旦设置了指针,它们就无法更改。

您可以通过以下方式实现您想要的:

template<typename Type>
struct T {
    T(Type* a, Type* b): 
        a(a), b(b) {}

    Type* a;
    Type* b;
};

然后:

int a = 1, b = 2;
int const* aRef = &a;
int const* bRef = &b;
T<int const> obj(aRef, bRef);

Live demo

如果您还希望指针本身为const,那么您可以使用:

T<int const> const obj(aRef, bRef);

【讨论】:

  • 不幸的是,这是一个 hack --- 我需要 T 类型的对象以非平凡的方式相互交互(例如,我需要 T::Op(const& T rhs) 之类的东西在模板中进一步使用),因此这种解决方法将很快解开。相比之下,在我看来, const-cast 是更优雅的解决方案。
  • @random 等等,这个解决方案是“黑客”,但const_cast 很优雅?还有为什么会解开?您可以使 rhs 本身采用模板参数。例如template&lt;typename Type&gt; T::Op(T&lt;Type&gt; const&amp; rhs).
  • 是的,const_cast 也是一个 hack,但如果我可以这么说的话,它是一个简单直接的 hack。是的,我可以把它模板化,甚至应该,但是当我说“解开”时,我的意思是我必须在每一个转折点上考虑这个模板化的替代方案,当我真的只想要一个 strongly const T 对象时,传播的 const 限定符。不要误会我的意思,我同意您可以使用您建议的代码(如const_cast)实现技术上有效的代码。
  • @random 如果我要使用 a hack,我更喜欢较短的。到时候已经无所谓了。
  • @random 请在您的问题中添加互动示例。也许我们可以帮助制定一个好的解决方案。我忍不住想,这个设计上的某些东西让我闻起来很腥。放弃 const 可能会导致隐藏的错误,因为编译器可能依赖于 const 性并添加对象在更深层次上没有改变
【解决方案2】:

你可以像这样使用继承:

struct CT
{
    CT(int const * a, int const * b) :
        a(a), b(b) {}

    int const * ca;
    int const * cb;
};

struct T : CT
{
    T(int * a, int * b) :
        CT(a, b) {}

    int * a() const { return const_cast<int *>(ca); }
    int * b() const { return const_cast<int *>(cb); }    
};

或者您可以将CTT 设为不相关的类型,并提供从TCT 的隐式转换。

我真的不想通过const TT 进行此操作。这是不直观的。此外,您还必须禁用默认的 copy-ctor。否则,总是可以从 const-ref 创建一个非常量副本,然后使用该非常量副本修改 *a*b

【讨论】:

    【解决方案3】:

    我想我明白了。你想先用你自己的可写存储int*调用ArrayWithMax,然后你将用你收到的非可变存储int const *调用它。然后,您将这些不可变的ArrayWithMax 传递给可变的ArrayWithMax::Max() 以评估您的运行​​最大值。假设这是您想要的,我有点同意 vlad_tepesch 的观点,因为设计可能需要一些工作。也就是说,我会用这样的模板来做:

    #include <iostream>
    #include <algorithm>
    #include <cstring>
    #include <iterator>
    
    using namespace std;
    
    const size_t ARRAY_SIZE = 2;   // hardcoded for now; it could be 1000
    
    template <typename T>
    class ArrayWithMax
    {
    public:
        ArrayWithMax(T array) : array_(array){}
    
        // Max will accept any class, as long as it provides U::begin() const
        // Max will not be generated when type T is const.
        template <typename U>
        void Max(const U& rhs)
        {
            transform(begin(), end(), rhs.begin(), begin(),
                [] (auto a, auto b) { return max(a, b); } );
        }
    
        T begin() { return array_; }
        T end() { return array_+size(); }
    
        T begin() const { return array_; }
        T end() const { return array_+size(); }
        size_t size() const { return ARRAY_SIZE; }
    
    private:
        T array_;
    };
    
    int main()
    {
        // some data to test with
        int someArray[ARRAY_SIZE];
        someArray[0] = 1;
        someArray[1] = 2;
        int const * received = someArray;
    
        int someArray2[ARRAY_SIZE];
        someArray2[0] = 0;
        someArray2[1] = 8;
        int const * received2 = someArray2;
    
        // to store max
        int myStorage[ARRAY_SIZE];
        memset(myStorage, 0, sizeof(myStorage));
    
        int* aWithMax = myStorage;
    
        // first mutable ArrayWithMax with myStorage
        ArrayWithMax<int*> a(aWithMax);
    
        copy(a.begin(), a.end(), ostream_iterator<int>(cout, " "));
        cout << endl;
    
        // other ArrayWithMax with const int*
        ArrayWithMax<const int*> receivedArray(received);
        a.Max(receivedArray);
    
        copy(a.begin(), a.end(), ostream_iterator<int>(cout, " "));
        cout << endl;
    
        // and another
        ArrayWithMax<const int*> receivedArray2(received2);
        a.Max(receivedArray2);
    
        copy(a.begin(), a.end(), ostream_iterator<int>(cout, " "));
        cout << endl;
    }
    

    该程序编译如下所示,它产生以下输出:

    Luiss-Air:const luis$ g++-5.3.0 -std=c++14 ArrayWithMax.C 
    Luiss-Air:const luis$ a.out
    0 0 
    1 2 
    1 8 
    Luiss-Air:const luis$
    

    如果ArrayWithMax 仅计算运行最大值,请考虑仅使用 std::transform:

        // this does what you want to do with your class
        transform(aWithMax, aWithMax+ARRAY_SIZE, received, aWithMax,
            [] (auto a, auto b) { return max(a, b); });
    

    【讨论】:

    • Luis,请看我刚刚添加的示例。我不确定我是否了解如何修改您的代码以达到预期的结果。
    • @random,请参阅我的答案的编辑。我假设您应该修改他们传递给您的数组。如果是这样,他们为什么要通过你const int*
    • 还有,你怎么知道数组有多大?
    • 不,我没有修改收到的数组(我按顺序收到了几个)。我确实想创建自己的数组,并“应用”(通过 ::Max)我传递给这个数组的数组。
    • @random,我再次编辑了我的回复。希望这次我明白你想要什么。
    猜你喜欢
    • 1970-01-01
    • 2010-09-18
    • 2012-11-07
    • 2014-11-26
    • 1970-01-01
    • 2021-07-25
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多