简体   繁体   English

如何用 const 值初始化非 const 成员变量

[英]How to initialize non-const member variable with const value

struct IntSlice {
  int* ptr_;
  int  len_;
};

std::initializer_list<int> xs = {1, 2, 3};
const IntSlice s = {xs.begin(), (int)xs.size()}; // this does not work :(

It is giving me an error, that we cannot assign a const pointer to a non const pointer but I thought the declaration const IntSlice would fix that.它给了我一个错误,我们不能将 const 指针分配给非 const 指针,但我认为声明const IntSlice可以解决这个问题。

I think I know what's going on.我想我知道发生了什么。 const IntSlice is promoting int* ptr_ to int* const ptr_ not const int* const ptr_ which is what I want. const IntSliceint* ptr_int* const ptr_而不是const int* const ptr_这是我想要的。

But surely, there has to be some way to promote int* ptr_ to const int* without having to create a ReadonlyIntSlice ?但可以肯定的是,必须有某种方法将int* ptr_const int*而不必创建ReadonlyIntSlice

The members of an object with const -qualified class type still have the same type as declared in the class.具有const限定的 class 类型的 object 的成员仍具有与 class 中声明的类型相同的类型。 The type of the ptr member subobject of const IntSlice s is int* , not int* const or const int* . const IntSlice s ptr成员子对象的类型是int* ,而不是int* constconst int*

However when using the name of a variable which is declared const as an expression, then it is a lvalue expression and the expression's type is also const -qualified.但是,当使用声明为const作为表达式的变量的名称时,它是一个左值表达式,并且表达式的类型也是const限定的。 When accessing a non-static data member via a member access expression like s.ptr , the const on the object expression ( s ) is propagated as top-level const to the type of whole member access expression.通过s.ptr之类的成员访问表达式访问非静态数据成员时,object 表达式 ( s ) 上的const作为顶级const传播到整个成员访问表达式的类型。 So the type of the expression s.ptr would be int* const , although the member subobject ptr has type int* .所以表达式s.ptr的类型将是int* const ,尽管成员子对象ptr的类型是int*

But surely, there has to be some way to promote int* ptr_ to const int* without having to create a ReadonlyIntSlice ?但可以肯定的是,必须有某种方法将int* ptr_const int*而不必创建ReadonlyIntSlice

If you want the type of a member to change, then you must make a new class type for that.如果要更改成员的类型,则必须为此创建新的 class 类型。 However, you do not need to completely rewrite classes in C++ for every minor modification that you'd like to make.但是,对于您想要进行的每个小修改,您不需要完全重写 C++ 中的类。 That's what templates are for:这就是模板的用途:

template<typename T>
struct IntSlice {
  T* ptr_;
  int len_;
};

And now you can use IntSlice<int> for modifiable pointers and IntSlice<const int> for non-modifiable pointers.现在您可以将IntSlice<int>用于可修改指针, IntSlice<const int>用于不可修改指针。


You probably added the explicit cast in (int)xs.size() because the compiler complained about the initialization without it.您可能在(int)xs.size()中添加了显式转换,因为编译器抱怨没有它的初始化。 There is a good reason for that.这是有充分理由的。 When using list-initialization (initialization with braces), then C++ does not allow narrowing conversions to happen in the initializer as a protection against mistakes.当使用列表初始化(带大括号的初始化)时,C++ 不允许在初始化程序中发生缩小转换以防止错误。 The type of xs.size() is std::size_t . xs.size()的类型是std::size_t int is not typically able to hold all values that xs.size() could have. int通常不能保存xs.size()可能具有的所有值。

The point therefore is that, instead of performing the cast, you should have the len_ member be of type std::size_t or std::ptrdiff_t (both may be reasonable, although the compiler will however still complain about narrowing in the latter case).因此,重点是,您应该让len_成员为std::size_tstd::ptrdiff_t类型,而不是执行强制转换(两者都可能是合理的,尽管编译器仍然会抱怨在后一种情况下会缩小) . These types are guaranteed to be able to hold all sizes that a pointer range could possibly have.这些类型保证能够容纳指针范围可能具有的所有大小。 (But the compiler doesn't know that sz.size() can't be so large that it will narrow to std::ptrdiff_t .) (但编译器不知道sz.size()不能太大以至于它会缩小到std::ptrdiff_t 。)

Of course in your example you know that xs is of a size fitting int .当然,在您的示例中,您知道xs的尺寸适合int If you want to instead rely on that, you should mark xs as constexpr .如果您想依赖它,则应将xs标记为constexpr This let's the compiler know that it is to be treated as a compile-time constant and then xs.size() is also a compile-time constant, in which case the narrowing rules are relaxed to make the compiler complain only if the actual compile-time value doesn't fit the target type.这让编译器知道它将被视为编译时常量,然后xs.size()也是编译时常量,在这种情况下,放宽缩小规则以使编译器仅在实际编译时才抱怨-time 值不适合目标类型。


You don't really need to write IntSlice at all though.不过,您根本不需要编写IntSlice Since C++20 the standard library has std::span for exactly that purpose with a richer interface.自 C++20 以来,标准库具有std::span用于该目的,并具有更丰富的接口。 If you don't have C++20, it is not difficult to re-implement most of it.如果你没有 C++20,重新实现它的大部分并不难。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM