使用一个优于另一个是否有任何优势:
class Foo
{
public:
const int& get() const
{
// stuff here
return myInt;
}
int& get()
{
return const_cast<int&>(static_cast<const Foo*>(this)->get());
}
};
或者
class Foo
{
public:
int& get()
{
// stuff here
return myInt;
}
const int& get() const
{
return const_cast<Foo*>(this)->get();
}
};
我只使用了第一个,但我刚看到第二个用在某个地方,所以我很想知道。
注释// stuff here
可能是一个非平凡的检查,例如检索表的索引以便返回表的成员的引用(例如:myInt = myTable[myComputedIndex];
)所以我不能只是把它公之于众。因此,表和任何成员都不是const。
答案 0 :(得分:20)
如果你必须创建一个与const-anostic不相关的函数,并避免重复,一个巧妙的方法是将实现委托给模板,例如
class Foo {
private:
int my_int;
template <typename ThisPtr>
static auto& get(ThisPtr this_ptr) {
return this_ptr->my_int;
}
public:
int& get() {
return get(this);
}
const int& get() const {
return get(this);
}
};
通过这种方式,您可以避免使用const_cast
,mutable
以及在此类情况下尝试减少代码重复的其他内容。如果您遇到问题,编译器会通知您。
答案 1 :(得分:9)
忽略您是否确实需要getter的问题,在const
和非const
方法中复制功能时的最佳解决方案是使用非const
方法调用const
方法并抛弃结果的const
- 即你在问题中提出的两个替代方案中的第一个。
原因很简单:如果你这样做(使用非const
方法中的逻辑),你可能会意外地最终修改const
对象,并且编译器赢了在编译时捕获它(因为该方法未声明const
) - 这将具有未定义的行为。
当然,如果“getter”实际上不是一个getter(例如,如果它做的事情比仅返回对私有字段的引用更复杂的话),这只是一个问题。
此外,如果您不受C ++ 11约束,Curious在其答案中提供的基于模板的解决方案是避免此问题的另一种方法。
答案 2 :(得分:2)
使用一个优于另一个是否有任何优势:...
不,两者都不好,因为它们违反了数据封装原则。
在您的示例中,您应该将myInt
作为公共成员。
对这种情况有吸引力是没有优势的。
如果你真的想要(需要)getter和setter函数,它们应该是这样的:
class Foo
{
private:
mutable int myInt_;
// ^^^^^^^ Allows lazy initialization from within the const getter,
// simply omit that if you dont need it.
public:
void myInt(int value)
{
// Do other stuff ...
myInt = value;
// Do more stuff ...
}
const int& myInt() const
{
// Do other stuff ...
return myInt_;
}
}
答案 3 :(得分:1)
您没有说myInt
来自哪里,最佳答案取决于。
有2 + 1种可能的情况:
1)最常见的情况是myInt
来自类内部的指针。
假设这是避免代码重复和转换的最佳解决方案。
class Foo{
int* myIntP;
...
int& get_impl() const{
... lots of code
return *myIntP; // even if Foo instance is const, *myInt is not
}
public:
int& get(){return get_impl();}
const int& get() const{return get_impl();}
};
上述情况适用于指针数组和(大多数)智能指针。
2)另一个常见的情况是myInt
是引用或值成员,那么之前的解决方案不起作用。
但是根本不需要getter
的情况也是如此。
在这种情况下不要使用吸气剂。
class Foo{
public:
int myInt; // or int& myInt;
};
完成! :)
3)@Aconcagua指出的第三种情况是内部固定数组。在这种情况下,这是一个折腾,它真的取决于你在做什么,如果找到索引确实是问题,那么可以将其考虑在内。然而,目前尚不清楚申请是什么:
class Foo{
int myInts[32];
...
int complicated_index() const{...long code...}
public:
int& get(){return myInts[complicated_index()];}
const int& get() const{return myInts[complicated_index()];}
};
我的观点是,了解问题并且不要过度工程。 <{1}}或模板不需要解决此问题。
以下完整的工作代码:
const_cast
答案 4 :(得分:0)
当您打算访问更复杂的内部结构时(通过编辑澄清;例如为std :: vector做内部数组提供operator[](size_t index)
),那么您必须确保不这样做通过修改可能的const对象来调用未定义的行为。
在第二种方法中,这样做的风险更高:
int& get()
{
// stuff here: if you modify the object, the compiler won't warn you!
// but you will modify a const object, if the other getter is called on one!!!
return myInt;
}
在第一个版本中,你是安全的(除非你在这里做const_cast
,现在真的很糟糕......),这是 这种方法的优点:
const int& get() const
{
// stuff here: you cannot modify by accident...
// if you try, the compiler will complain about
return myInt;
}
如果您实际上需要来修改非const getter中的对象,那么无论如何都不能有一个通用的实现......
答案 5 :(得分:-2)
通过非const访问路径[...]修改const对象会导致未定义的行为。
(资料来源:http://en.cppreference.com/w/cpp/language/const_cast)
这意味着如果myInt
实际上是Foo
的const成员,则第一个版本可能导致未定义的行为:
class Foo
{
int const myInt;
public:
const int& get() const
{
return myInt;
}
int& get()
{
return const_cast<int&>(static_cast<const Foo*>(this)->get());
}
};
int main()
{
Foo f;
f.get() = 10; // this compiles, but it is undefined behavior
}
第二个版本无法编译,因为get
的非常量版本格式不正确:
class Foo
{
int const myInt;
public:
int& get()
{
return myInt;
// this will not compile, you cannot return a const member
// from a non-const member function
}
const int& get() const
{
return const_cast<Foo*>(this)->get();
}
};
int main()
{
Foo f;
f.get() = 10; // get() is ill-formed, so this does not compile
}
此版本实际上是由Scott Meyers在有效C ++ 中避免在const
和非const
成员函数中重复使用。