如何 const_cast 一个常量指针向量指向一个非常量指针向量?
How to const_cast a vector of const pointers to a vector of non-const pointers?
我有一个函数正在自定义列表实现中查找元素。为了使其成为 const 正确的,我可以进行这种巧妙的重载,甚至可以使用单行代码来重用完全相同的实现,因此不必重复逻辑。
const MyObject* findObject(const MyList& list)
{
//...
}
MyObject* findObject(MyList& list)
{
return const_cast<MyObject*>(findObject(const_cast<const MyList&>(list)));
}
问题是,一旦我需要 return 向量内的多个元素指针,禁止 unsave/non-portable 像 reinterpret_cast 这样的 hack,我该怎么办?
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)) );
}
最简单的解决方案是忘记 const_cast
并显式实现两个重载。 这就是标准库实现所做的。
例如,std::vector
有 const
和 operator[]
的非 const
版本。 VC++ 和 GCC 都对该运算符有重复的实现(分别参见 include/vector
或 stl_vector.h
文件);两者都没有诉诸任何 const_cast
技巧来复制行为。
现在,如果您的 findObject
实施非常庞大和复杂,那么首选应该是使其更简单。作为临时解决方法,您可以考虑根据内部(私有或其他不可访问的)模板函数实现两个重载,使用 decltype
尾随 return 类型来获得正确的 const
或非-const
return 通过参数输入。这是一个简单的例子:
#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);
}
示例输出(取决于 typeid
在您的实施中产生的名称类型):
non-const version
struct MyObject *
const version
struct MyObject const *
但坦率地说,我不会这样做。它似乎设计过度,也许有点太聪明了。过于聪明的代码很少是个好主意,因为它会使人感到困惑。
选项 1
如果你愿意支付循环+复制价格(对比copy-pasting代码):
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;
选项 2
[edit] 如果您不想支付循环 + 复制价格,这是另一种选择。
假设您正在使用 cpp 文件(不是 header-only)。在你的编译单元 (.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;
}
为了保持整洁 "hidden",您可以在 class.
中将该功能添加为好友
template <class T, class MyObj>
friend std::vector<T*> getter_imp(MyObj&, int);
现在,您可以从 const 和 non-const 成员函数中调用 getter。没有 copy-paste(也就是我们生活的祸根)。
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);
}
这里发生了什么?
您在模板参数中编码 const
。假设您从 const
函数调用 T = const int
和 MyObj = const your_class
。在函数内部,const
也被推导为 auto&
。
从非 const
上下文调用时,模板参数和 auto&
不是 const
。所以它有效。
我有一个函数正在自定义列表实现中查找元素。为了使其成为 const 正确的,我可以进行这种巧妙的重载,甚至可以使用单行代码来重用完全相同的实现,因此不必重复逻辑。
const MyObject* findObject(const MyList& list)
{
//...
}
MyObject* findObject(MyList& list)
{
return const_cast<MyObject*>(findObject(const_cast<const MyList&>(list)));
}
问题是,一旦我需要 return 向量内的多个元素指针,禁止 unsave/non-portable 像 reinterpret_cast 这样的 hack,我该怎么办?
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)) );
}
最简单的解决方案是忘记 const_cast
并显式实现两个重载。 这就是标准库实现所做的。
例如,std::vector
有 const
和 operator[]
的非 const
版本。 VC++ 和 GCC 都对该运算符有重复的实现(分别参见 include/vector
或 stl_vector.h
文件);两者都没有诉诸任何 const_cast
技巧来复制行为。
现在,如果您的 findObject
实施非常庞大和复杂,那么首选应该是使其更简单。作为临时解决方法,您可以考虑根据内部(私有或其他不可访问的)模板函数实现两个重载,使用 decltype
尾随 return 类型来获得正确的 const
或非-const
return 通过参数输入。这是一个简单的例子:
#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);
}
示例输出(取决于 typeid
在您的实施中产生的名称类型):
non-const version
struct MyObject *
const version
struct MyObject const *
但坦率地说,我不会这样做。它似乎设计过度,也许有点太聪明了。过于聪明的代码很少是个好主意,因为它会使人感到困惑。
选项 1
如果你愿意支付循环+复制价格(对比copy-pasting代码):
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;
选项 2
[edit] 如果您不想支付循环 + 复制价格,这是另一种选择。
假设您正在使用 cpp 文件(不是 header-only)。在你的编译单元 (.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;
}
为了保持整洁 "hidden",您可以在 class.
中将该功能添加为好友template <class T, class MyObj>
friend std::vector<T*> getter_imp(MyObj&, int);
现在,您可以从 const 和 non-const 成员函数中调用 getter。没有 copy-paste(也就是我们生活的祸根)。
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);
}
这里发生了什么?
您在模板参数中编码 const
。假设您从 const
函数调用 T = const int
和 MyObj = const your_class
。在函数内部,const
也被推导为 auto&
。
从非 const
上下文调用时,模板参数和 auto&
不是 const
。所以它有效。