C++ 如何将常量指针向量转换为非常量指针向量?
我有一个函数,它在自定义列表实现中查找元素。为了使它保持正确,我可以完成这个整洁的重载,甚至可以使用一行程序来重用相同的实现,因此不必重复逻辑C++ 如何将常量指针向量转换为非常量指针向量?,c++,pointers,vector,const-cast,C++,Pointers,Vector,Const Cast,我有一个函数,它在自定义列表实现中查找元素。为了使它保持正确,我可以完成这个整洁的重载,甚至可以使用一行程序来重用相同的实现,因此不必重复逻辑 const MyObject* findObject(const MyList& list) { //... } MyObject* findObject(MyList& list) { return const_cast<MyObject*>(findObject(const_cast<const MyLis
const MyObject* findObject(const MyList& list)
{
//...
}
MyObject* findObject(MyList& list)
{
return const_cast<MyObject*>(findObject(const_cast<const MyList&>(list)));
}
constmyobject*findObject(constmylist&list)
{
//...
}
MyObject*findObject(MyList和list)
{
返回const_cast(findObject(const_cast(list));
}
问题是,一旦我需要在向量中返回多个元素指针,我该怎么做,以阻止像reinterpret_cast这样的未保存/不可移植的黑客行为
std::vector<const MyObject*> findObject(const MyList& list)
{
//...
}
std::vector<MyObject*> findObject(MyList& list)
{
// this is sth I'm looking for:
const_element_cast<std::vector<MyObject*>>( findObject( const_cast<const MyList&>(list)) );
}
std::vector findObject(const MyList&list)
{
//...
}
std::vector findObject(MyList和list)
{
//这是我正在寻找的东西:
常量元素转换(findObject(常量转换(列表));
}
最简单的解决方案是忘记const\u cast
,直接实现这两个重载这是标准库实现的功能。
例如,std::vector
具有const
和操作符[]
的非const
版本。VC++和GCC都有该操作符的重复实现(分别参见include/vector
或stl_vector.h
文件);他们都不会使用任何const_cast
技巧来复制这种行为
现在,如果您的findObject
实现非常大和复杂,那么第一个选择应该是简化它。作为一个临时解决方案,您可以考虑在内部(私有或其他不可访问)模板函数的基础上实现重载,使用<代码> DeCytys/COD>尾随返回类型来获得正确的<代码> const < />代码>或非->代码> const < /Cord>返回类型。下面是一个简单的例子:
#include <iostream>
#include <vector>
#include <typeinfo> // just to demonstrate what's going on
// simple example data structure:
struct MyObject {};
struct MyList
{
MyObject elements[3] = { MyObject {}, MyObject {}, MyObject {} };
};
namespace internal
{
// &list.elements[0] will be either MyObject const* or MyObject*
template <class ConstOrNonConstList>
auto doFindObject(ConstOrNonConstList& list) -> std::vector<decltype(&list.elements[0])>
{
// let's say there was an immensely complicated function here,
// with a lot of error potential and maintenance nightmares
// lurking behind a simple copy & paste solution...
// just to demonstrate what's going:
std::cout << typeid(decltype(&list.elements[0])).name() << "\n";
std::vector<decltype(&list.elements[0])> result;
for (auto&& element : list.elements)
{
result.push_back(&element);
}
return result;
}
}
std::vector<const MyObject*> findObject(const MyList& list)
{
std::cout << "const version\n";
return internal::doFindObject(list);
}
std::vector<MyObject*> findObject(MyList& list)
{
std::cout << "non-const version\n";
return internal::doFindObject(list);
}
int main()
{
MyList list1;
MyList const list2;
auto obj1 = findObject(list1);
auto obj2 = findObject(list2);
}
但坦率地说,我不会这么做。它似乎设计过度,而且可能有点太聪明了。过于聪明的代码很少是个好主意,因为它会让人困惑。选项1 如果您愿意支付循环+复制价格(相对于复制粘贴代码):
std::vector c_vec=static_cast(this)->my_const_func();
std::载体ret;
for(const thing*t:c_vec){
//是的,你可以这样做,因为这个函数不是常量
//因此,保证您处于非常量上下文中。
//见斯科特·迈尔斯。
后推回(连续铸造(t));
}
返回ret;
选项2
[编辑]如果您不想支付循环+复制价格,这里有另一种选择
假设您使用的是cpp文件(不仅仅是头文件)。在编译单元(.cpp)中,添加一个模板化的getter
template <class T, class MyObj>
std::vector<T*> getter_imp(MyObj& obj, int someval) {
assert(someval <= 42);
std::vector<T*> ret;
for (auto& i : obj._my_vec) {
if (i < someval) {
ret.push_back(&i);
}
}
return ret;
}
模板
向量getter_imp(MyObj&obj,int-someval){
assert(someval)const_cast(list)
参数上的const
说明符是对调用方的一种保证,即函数不会更改传递给它的数据。这不是要传递的数据的要求。您可以传递非const数据。因此您的const_cast(list)
是无意义的。另外,从findObject()返回const MyObject*
也是毫无意义的。为了理解为什么,请考虑这个代码:<代码> const int c=42;int i= c;< /c> >正确地写下它并悄悄地远离这些幼稚的const铸造思想。你知道为什么他们会在枪上安上安全的抓取器,对吗?@舍尔吉避免了无限递归?@ Rok错了。这是借用Scott Meyer的有效C++的技术。F库设计者避免了它的缺点是什么?到目前为止,我真的看不到任何东西。即使是太大,也总是不可能重整整个方法。例如,考虑处理已测试的遗留代码。尽管如此,即使它最终只限于边界检查和其他基础知识,我还是希望它一次性完成并使用。两次,而不是在两个位置复制,如果开发人员进行更改,则依赖开发人员同步这两个位置(实际经验表明:他通常不会)
std::vector<const thing*> c_vec = static_cast<const me*>(this)->my_const_func();
std::vector<thing*> ret;
for (const thing* t : c_vec) {
// Yes you can do this, since this func isn't const
// so you are guaranteed to be inside non-const context.
// See Scott Meyers.
ret.push_back(const_cast<thing*>(t));
}
return ret;
template <class T, class MyObj>
std::vector<T*> getter_imp(MyObj& obj, int someval) {
assert(someval <= 42);
std::vector<T*> ret;
for (auto& i : obj._my_vec) {
if (i < someval) {
ret.push_back(&i);
}
}
return ret;
}
template <class T, class MyObj>
friend std::vector<T*> getter_imp(MyObj&, int);
std::vector<const int*> get(int v) const {
return getter_imp<const int>(*this, v);
}
std::vector<int*> get(int v) {
return getter_imp<int>(*this, v);
}