[英]C++: STL troubles with const class members
It is an open ended question. 这是一个开放式的问题。 Effective C++.
有效的C ++。 Item 3. Use const whenever possible.
第3项。 尽可能使用const。 Really?
真?
I would like to make anything which doesn't change during the objects lifetime const. 我想做一些在对象生命周期const期间不会改变的东西。 But const comes with it own troubles.
但const带来了它自己的麻烦。 If a class has any const member, the compiler generated assignment operator is disabled.
如果类具有任何const成员,则禁用编译器生成的赋值运算符。 Without an assignment operator a class won't work with STL.
如果没有赋值运算符,则类将无法与STL一起使用。 If you want to provide your own assignment operator, const_cast is required.
如果要提供自己的赋值运算符,则需要const_cast 。 That means more hustle and more room for error.
这意味着更多的喧嚣和更多的错误空间。 How often you use const class members?
你经常使用const类成员?
EDIT: As a rule, I strive for const correctness because I do a lot of multithreading. 编辑:作为一项规则,我努力保持const的正确性,因为我做了很多多线程。 I rarely need to implemented copy control for my classes and never code delete (unless it is absolutely necessary).
我很少需要为我的类实现复制控制,从不编写删除代码(除非绝对必要)。 I feel that the current state of affairs with const contradicts my coding style.
我觉得const的当前状态与我的编码风格相矛盾。 Const forces me to implement assignment operator even though I don't need one.
Const迫使我实现赋值运算符,即使我不需要它。 Even without const_cast assignment is a hassle.
即使没有const_cast分配也很麻烦。 You need to make sure that all const members compare equal and then manually copy all non-const member.
您需要确保所有const成员比较相等,然后手动复制所有非const成员。
Code. 码。 Hope it will clarify what I mean.
希望它能澄清我的意思。 The class you see below won't work with STL.
您在下面看到的课程不适用于STL。 You need to implement an assignment for it, even though you don't need one.
您需要为它实现一个赋值,即使您不需要它。
class Multiply {
public:
Multiply(double coef) : coef_(coef) {}
double operator()(double x) const {
return coef_*x;
}
private:
const double coef_;
};
You said yourself that you make const "anything which doesn't change during the objects lifetime". 你自己说过你使const“在物体生命期间不会改变的任何东西”。 Yet you complain about the implicitly declared assignment operator getting disabled.
然而,您抱怨隐式声明的赋值运算符被禁用。 But implicitly declared assignment operator does change the contents of the member in question!
但隐式声明的赋值运算符确实改变了有问题的成员的内容! It is perfectly logical (according to your own logic) that it is getting disabled.
完全合乎逻辑(根据您自己的逻辑),它正在被禁用。 Either that, or you shouldn't be declaring that member const.
要么是,要么你不应该声明成员const。
Also, providing you own assignment operator does not require a const_cast
. 此外,提供自己的赋值运算符不需要
const_cast
。 Why? 为什么? Are you trying to assign to the member you declared const inside your assignment operator?
您是否尝试在赋值运算符中指定您声明为const的成员? If so, why did you declare it const then?
如果是这样,为什么你声明它为const呢?
In other words, provide a more meaningful description of the problems you are running into. 换句话说,提供您正在遇到的问题的更有意义的描述。 The one you provided so far is self-contradictory in the most obvious manner.
到目前为止你提供的那个以最明显的方式是自相矛盾的。
I very rarely use them - the hassle is too great. 我很少使用它们 - 麻烦太大了。 Of course I always strive for const correctness when it comes to member functions, parameters or return types.
当然,在成员函数,参数或返回类型方面,我总是力求const正确性。
As AndreyT pointed out, under these circumstances assignment (mostly) doesn't make a lot of sense. 正如AndreyT指出的那样,在这些情况下,分配(大多数)并没有多大意义。 The problem is that
vector
(for one example) is kind of an exception to that rule. 问题是
vector
(例如)是该规则的一个例外。
Logically, you copy an object into the vector
, and sometime later you get back another copy of the original object. 从逻辑上讲,您将对象复制到
vector
,稍后您将获得原始对象的另一个副本。 From a purely logical viewpoint, there's no assignment involved. 从纯粹的逻辑观点来看,不涉及任务。 The problem is that
vector
requires that the object be assignable anyway (actually, all C++ containers do). 问题是
vector
要求对象无论如何都是可分配的(实际上,所有C ++容器都可以)。 It's basically making an implementation detail (that somewhere in its code, it might assign the objects instead of copying them) part of the interface. 它基本上是一个实现细节(在代码中的某个地方,它可能分配对象而不是复制它们)的一部分接口。
There is no simple cure for this. 对此没有简单的治疗方法。 Even defining your own assignment operator and using
const_cast
doesn't really fix the problem. 即使定义自己的赋值运算符并使用
const_cast
也无法解决问题。 It's perfectly safe to use const_cast
when you get a const
pointer or reference to an object that you know isn't actually defined to be const
. 当你获得一个
const
指针或对一个你知道实际上没有被定义为const
的对象的引用时,使用const_cast
是完全安全的。 In this case, however, the variable itself is defined to be const
-- attempting to cast away the const
ness and assign to it gives undefined behavior. 但是,在这种情况下,变量本身被定义为
const
- 试图抛弃const
并赋予它给出未定义的行为。 In reality, it'll almost always work anyway (as long as it's not static const
with an initializer that's known at compile time), but there's no guarantee of it. 实际上,它几乎总是可以工作(只要它不是带有在编译时已知的初始化器的
static const
),但是不能保证它。
C++ 11 and newer add a few new twists to this situation. C ++ 11和更新版本为这种情况添加了一些新的曲折。 In particular, objects no longer need to be assignable to be stored in a vector (or other collections).
特别是,不再需要将对象分配以存储在向量(或其他集合)中。 It's sufficient that they be movable.
它们可以移动就足够了。 That doesn't help in this particular case (it's no easier to move a
const
object than it is to assign it) but does make life substantially easier in some other cases (ie, there are certainly types that are movable but not assignable/copyable). 这在这种特殊情况下没有帮助(移动
const
对象比分配它更容易)但是在其他一些情况下确实使生活变得更加容易(例如,肯定有可移动但不可分配/可复制的类型)。
In this case, you could use a move rather than a copy by adding a level of indirection. 在这种情况下,您可以通过添加间接级别来使用移动而不是副本。 If your create an "outer" and an "inner" object, with the
const
member in the inner object, and the outer object just containing a pointer to the inner: 如果你创建一个“外部”和一个“内部”对象,内部对象中的
const
成员和外部对象只包含指向内部的指针:
struct outer {
struct inner {
const double coeff;
};
inner *i;
};
...then when we create an instance of outer
, we define an inner
object to hold the const
data. ...然后当我们创建一个
outer
实例时,我们定义一个inner
对象来保存const
数据。 When we need to do an assignment, we do a typical move assignment: copy the pointer from the old object to the new one, and (probably) set the pointer in the old object to a nullptr, so when it's destroyed, it won't try to destroy the inner object. 当我们需要做一个赋值时,我们做一个典型的移动赋值:将指针从旧对象复制到新对象,并且(可能)将旧对象中的指针设置为nullptr,所以当它被销毁时,它就赢了试着破坏内部物体。
If you wanted to badly enough, you could use (sort of) the same technique in older versions of C++. 如果你想要足够严重,你可以在旧版本的C ++中使用(类似)相同的技术。 You'd still use the outer/inner classes, but each assignment would allocate a whole new inner object, or you'd use something like a shared_ptr to let the outer instances share access to a single inner object, and clean it up when the last outer object is destroyed.
您仍然使用外部/内部类,但每个赋值将分配一个全新的内部对象,或者您使用类似shared_ptr的东西让外部实例共享对单个内部对象的访问权限,并在最后一个外部物体被摧毁
It doesn't make any real difference, but at least for the assignment used in managing a vector, you'd only have two references to an inner
while the vector
was resizing itself (resizing is why a vector requires assignable to start with). 它没有任何真正的区别,但至少对于管理向量时使用的赋值,在
vector
调整自身时,你只有两个对inner
引用(调整大小是为什么向量需要赋值可以开始)。
Errors at compile time are painful, but errors at runtime are deadly. 编译时的错误很痛苦,但运行时的错误是致命的。 Constructions using const might be a hassle to code, but it might help you find bugs before you implement them.
使用const的构造可能是代码的麻烦,但它可能会帮助您在实现它们之前找到错误。 I use consts whenever possible.
我尽可能使用consts。
I try my best to follow the advice of using const
whenever possible, however I agree that when it comes to class members, const
is a big hassle. 我尽可能地遵循使用
const
的建议,但我同意,当涉及到班级成员时, const
是一个很大的麻烦。
I have found that I am very careful with const
-correctness when it comes to parameters, but not as much with class members. 我发现在参数方面我非常小心
const
-correctness,但对于类成员却没有那么多。 Indeed, when I make class members const
and it results in an error (due to using STL containers), the first thing I do is remove the const
. 实际上,当我使类成员
const
并且它导致错误时(由于使用STL容器),我做的第一件事就是删除const
。
I'm wondering about your case... Everything below is but supposition because you did not provide the example code describing your problem, so... 我想知道你的情况......以下所有内容都是假设,因为你没有提供描述你问题的示例代码,所以...
I guess you have something like: 我想你有类似的东西:
struct MyValue
{
int i ;
const int k ;
} ;
IIRC, the default assignment operator will do a member-by-member assignment, which is akin to : IIRC,默认赋值运算符将执行逐个成员的赋值,类似于:
MyValue & operator = (const MyValue & rhs)
{
this->i = rhs.i ;
this->k = rhs.k ; // THIS WON'T WORK BECAUSE K IS CONST
return *this ;
} ;
Thus, this won't get generated. 因此,这不会产生。
So, your problem is that without this assignment operator, the STL containers won't accept your object. 所以,你的问题是没有这个赋值运算符,STL容器将不接受你的对象。
As far I as see it: 就我所见:
operator =
operator =
I'm afraid to understand what do you mean by const_cast
. 我害怕明白
const_cast
是什么意思。
My own solution to your problem would be to write the following user defined operator : 我自己的问题解决方案是编写以下用户定义的运算符:
MyValue & operator = (const MyValue & rhs)
{
this->i = rhs.i ;
// DON'T COPY K. K IS CONST, SO IT SHOULD NO BE MODIFIED.
return *this ;
} ;
This way, if you'll have: 这样,如果你有:
MyValue a = { 1, 2 }, b = {10, 20} ;
a = b ; // a is now { 10, 2 }
As far as I see it, it is coherent. 据我所知,它是连贯的。 But I guess, reading the
const_cast
solution, that you want to have something more like: 但我想,阅读
const_cast
解决方案,你想要更像的东西:
MyValue a = { 1, 2 }, b = {10, 20} ;
a = b ; // a is now { 10, 20 } : K WAS COPIED
Which means the following code for operator =
: 这意味着
operator =
的以下代码:
MyValue & operator = (const MyValue & rhs)
{
this->i = rhs.i ;
const_cast<int &>(this->k) = rhs.k ;
return *this ;
} ;
But, then, you wrote in your question: 但是,那么,你在你的问题中写道:
I would like to make anything which doesn't change during the objects lifetime const
我想做一些在对象生命周期const期间不会改变的东西
With what I supposed is your own const_cast
solution, k changed during the object lifetime, which means that you contradict yourself because you need a member variable that doesn't change during the object lifetime unless you want it to change !
我假设你自己的
const_cast
解决方案,k在对象生命周期中发生了变化,这意味着你自相矛盾,因为你需要一个在对象生命周期内不会改变的成员变量, 除非你想要它改变 !
Accept the fact your member variable will change during the lifetime of its owner object, and remove the const. 接受您的成员变量在其所有者对象的生命周期内将更改的事实,并删除const。
you can store shared_ptr
to your const
objects in STL containers if you'd like to retain const
members. 如果您想保留
const
成员,可以将shared_ptr
存储到STL容器中的const
对象。
#include <iostream>
#include <boost/foreach.hpp>
#include <boost/make_shared.hpp>
#include <boost/shared_ptr.hpp>
#include <boost/utility.hpp>
#include <vector>
class Fruit : boost::noncopyable
{
public:
Fruit(
const std::string& name
) :
_name( name )
{
}
void eat() const { std::cout << "eating " << _name << std::endl; }
private:
const std::string _name;
};
int
main()
{
typedef boost::shared_ptr<const Fruit> FruitPtr;
typedef std::vector<FruitPtr> FruitVector;
FruitVector fruits;
fruits.push_back( boost::make_shared<Fruit>("apple") );
fruits.push_back( boost::make_shared<Fruit>("banana") );
fruits.push_back( boost::make_shared<Fruit>("orange") );
fruits.push_back( boost::make_shared<Fruit>("pear") );
BOOST_FOREACH( const FruitPtr& fruit, fruits ) {
fruit->eat();
}
return 0;
}
though, as others have pointed out it's somewhat of a hassle and often easier in my opinion to remove the const qualified members if you desire the compiler generated copy constructor. 但是,正如其他人已经指出的那样,如果你想要编译器生成的复制构造函数,那么在我看来删除const限定成员往往更容易。
I only use const on reference or pointer class members. 我只在引用或指针类成员上使用const。 I use it to indicate that the target of the reference or pointer should not be changed.
我用它来表明不应该改变引用或指针的目标。 Using it on other kinds of class members is a big hassle as you found out.
你发现,在其他类别的成员上使用它是一个很大的麻烦。
The best places to use const is in function parameters, pointers and references of all kinds, constant integers and temporary convenience values. 使用const的最佳位置是函数参数,指针和各种引用,常量整数和临时便利值。
An example of a temporary convenience variable would be: 临时便利变量的一个例子是:
char buf[256];
char * const buf_end = buf + sizeof(buf);
fill_buf(buf, buf_end);
const size_t len = strlen(buf);
That buf_end
pointer should never point anywhere else so making it const is a good idea. 那个
buf_end
指针永远不应该指向其他任何地方,所以使它成为const是一个好主意。 The same idea with len
. 与
len
相同的想法。 If the string inside buf
never changes in the rest of the function then its len
should not change either. 如果
buf
的字符串在函数的其余部分中永远不会改变,那么它的len
也不应该改变。 If I could, I would even change buf
to const after calling fill_buf
, but C/C++ does not let you do that. 如果可以,我甚至会在调用
fill_buf
之后将buf
更改为const,但C / C ++不会让你这样做。
The point is that the poster wants const
protection within his implementation but still wants the object assignable. 关键是海报希望在他的实现中保持
const
但仍然希望对象可分配。 The language does not support such semantics conveniently as constness of the member resides at the same logical level and is tightly coupled with assignability. 该语言不方便地支持这种语义,因为成员的constness位于相同的逻辑级别并且与可赋值性紧密耦合。
However, the pImpl
idiom with a reference counted implementation or smart pointer will do exactly what the poster wants as assignability is then moved out of the implementation and up a level to the higher level object. 然而,具有引用计数实现或智能指针的
pImpl
惯用语将完全符合海报的要求,因为可分配性随后被移出实现并向更高级别对象上升。 The implementation object is only constructed/destructed whence assignment is never needed at the lower level. 实现对象仅被构造/破坏,从而在较低级别永远不需要赋值。
I think your statement 我想你的说法
If a class has const any member, the compiler generated assignment operator is disabled.
如果类具有const任何成员,则禁用编译器生成的赋值运算符。
Might be incorrect. 可能不对。 I have classes that have const method
我有使用const方法的类
bool is_error(void) const;
....
virtual std::string info(void) const;
....
that are also used with STLs. 也用于STL。 So perhaps your observation is compiler dependent or only applicable to the member variables?
那么您的观察结果可能依赖于编译器还是仅适用于成员变量?
It isn't too hard. 这不是太难。 You shouldn't have any trouble making your own assignment operator.
制作自己的赋值运算符不会有任何问题。 The const bits don't need to be assigned (as they're const).
不需要分配const位(因为它们是const)。
Update 更新
There is some misunderstanding about what const means. 关于const的含义存在一些误解。 It means that it will not change, ever.
这意味着它永远不会改变。
If an assignment is supposed to change it, then it isn't const. 如果一个赋值应该改变它,那么它不是const。 If you just want to prevent others changing it, make it private and don't provide an update method.
如果您只是想阻止其他人更改它,请将其设为私有,并且不提供更新方法。
End Update 结束更新
class CTheta
{
public:
CTheta(int nVal)
: m_nVal(nVal), m_pi(3.142)
{
}
double GetPi() const { return m_pi; }
int GetVal() const { return m_nVal; }
CTheta &operator =(const CTheta &x)
{
if (this != &x)
{
m_nVal = x.GetVal();
}
return *this;
}
private:
int m_nVal;
const double m_pi;
};
bool operator < (const CTheta &lhs, const CTheta &rhs)
{
return lhs.GetVal() < rhs.GetVal();
}
int main()
{
std::vector<CTheta> v;
const size_t nMax(12);
for (size_t i=0; i<nMax; i++)
{
v.push_back(CTheta(::rand()));
}
std::sort(v.begin(), v.end());
std::vector<CTheta>::const_iterator itr;
for (itr=v.begin(); itr!=v.end(); ++itr)
{
std::cout << itr->GetVal() << " " << itr->GetPi() << std::endl;
}
return 0;
}
I would only use const member iff the class itself is non-copyable. 我只会使用const成员iff类本身是不可复制的。 I have many classes that I declare with boost::noncopyable
我用boost :: noncopyable声明了很多类
class Foo : public boost::noncopyable {
const int x;
const int y;
}
However if you want to be very sneaky and cause yourself lots of potential problems you can effect a copy construct without an assignment but you have to be a bit careful. 但是,如果你想要非常偷偷摸摸并且给自己带来很多潜在的问题,你可以在没有任务的情况下实现复制结构,但你必须要小心一点。
#include <new>
#include <iostream>
struct Foo {
Foo(int x):x(x){}
const int x;
friend std::ostream & operator << (std::ostream & os, Foo const & f ){
os << f.x;
return os;
}
};
int main(int, char * a[]){
Foo foo(1);
Foo bar(2);
std::cout << foo << std::endl;
std::cout << bar<< std::endl;
new(&bar)Foo(foo);
std::cout << foo << std::endl;
std::cout << bar << std::endl;
}
outputs 输出
1
2
1
1
foo has been copied to bar using the placement new operator. 已使用placement new运算符将foo复制到bar。
Philosophically speaking, it looks as safety-performance tradeoff. 从哲学上讲,它看起来像安全性能权衡。 Const used for safety.
Const用于安全。 As I understand, containers use assignment to reuse memory, ie for sake of performance.
据我所知,容器使用赋值来重用内存,即为了性能。 They would may use explicit destruction and placement new instead (and logicaly it is more correct), but assignment has a chance to be more efficient.
他们可能会使用显式销毁和替换新的替代(并且逻辑上它更正确),但是赋值有更高效的机会。 I suppose, it is logically redundant requirement "to be assignable" (copy constructable is enough), but stl containers want to be faster and simpler.
我认为,逻辑冗余要求“可分配”(复制可构造就足够了),但是stl容器希望更快更简单。
Of course, it is possible to implement assignment as explicit destruction+placement new to avoid const_cast hack 当然,可以将赋值实现为显式destroy + placement new以避免const_cast hack
You basically never want to put a const member variable in a class. 你基本上不想把一个const成员变量放在一个类中。 (Ditto with using references as members of a class.)
(同上使用引用作为类的成员。)
Constness is really intended for your program's control flow -- to prevent mutating objects at the wrong times in your code. Constness真正用于程序的控制流程 - 防止在代码中错误的时间发生变异。 So don't declare const member variables in your class's definition, rather make it all or nothing when you declare instances of the class.
因此,不要在类的定义中声明const成员变量,而是在声明类的实例时全部或全部使用它。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.