如何正确声明从非常量迭代器到指针的 const 指针
How to properly declare a const pointer from non-const iterator to pointer
背景
我正在实现一个模板过滤迭代器。给定任何类型的开始和结束迭代器,此迭代器将迭代范围并跳过一元谓词 returns 为假的任何元素。当然,我希望这个一元谓词始终有一个 const 参数,以避免谓词修改支持容器。
后备迭代器可以是任何类型和容器的迭代器。可以是基本类型,指针,引用,类。真的什么都有。
我 运行 遇到一个问题,我无法根据模板参数迭代器声明 std::function
以具有正确的 const
声明参数。我提炼了一个说明问题的最小代码示例。
代码
#include <vector>
#include <functional>
typedef std::vector<int*> vec_type;
typedef std::function<void(const vec_type::iterator::value_type&)> func_type;
void foo(vec_type& a, func_type f){
for (auto it = a.begin(); it != a.end(); ++it){
f(*it);
}
}
int main(int, char**){
vec_type v;
int a = 3;
int b = 4;
v.push_back(&a);
v.push_back(&b);
foo(v, [](int* x){*x = 0; });
return 0;
}
我预计在 lamda 上会出现编译错误,因为 int*
应该是 const int*
但 GCC 4.8.1 和 VS2013 都允许它并愉快地修改我认为将成为 const 的内容.
您的容器存储 int*
s。您的函数接受 "const reference to int*
"。这意味着指针指向可变数据。数据可以愉快地修改,因为你允许了。请注意 "degrees of constness" 之间的区别 - 您不能更改指针指向的内容,但可以修改它们指向的 int
。因此,要解决此问题,您的函数必须接受 "const reference to const int*
" 或 const int*
.
typedef std::function<void(const int*)> func_type;
...或者,如果您希望它更通用(请参阅 Yakk 的回答以获得更通用的解决方案):
#include <type_traits>
typedef std::vector<int*> vec_type;
typedef
std::add_pointer<
std::add_const<
std::remove_pointer<
vec_type::iterator::value_type
>::type
>::type
>::type value_t;
typedef std::function<void(const value_t&)> func_type;
当您修改指针指向的内容时,指针的容器不会被修改:容器拥有指针,而不是指向的对象。
但我理解 — 有时您希望 const
ness 向下传播指针。
这是一些模板元编程,它应该采用任何非 const
指针并使其成为 const
,以及其他一切都尽可能成为 const
。它以递归方式运行,处理引用(r 和 l 值)和对指针的引用到指向指针的指针的指针,几乎在任何地方都有或没有 const
或 volatile
修饰符。
template<class T>struct tag{using type=T;};
template<class X>
struct make_very_const:tag<const X> {};
template<class X>
using make_very_const_t=typename make_very_const<X>::type;
// makes code below easier to write (namely, the pointer code):
template<class X>
struct make_very_const<const X>:tag<const make_very_const_t<X>> {};
template<class X>
struct make_very_const<volatile X>:tag<const volatile make_very_const_t<X>> {};
template<class X>
struct make_very_const<const volatile X>:tag<const volatile make_very_const_t<X>> {};
// references:
template<class X>
struct make_very_const<X&>:tag<make_very_const_t<X>&>{};
template<class X>
struct make_very_const<X&&>:tag<make_very_const_t<X>&&>{};
// pointers:
template<class X>
struct make_very_const<X*>:tag<make_very_const_t<X>*const>{};
// std::reference_wrapper:
template<class X>
struct make_very_const<std::reference_wrapper<X>>:tag<std::reference_wrapper<make_very_const_t<X>>const>{};
这是一大堆废话。它如此冗长的原因是没有简单的方法来匹配 "type modifiers"(指针、const、volatile、引用等),所以你最终不得不非常具体和冗长。
但它在使用时为您提供了一种干净的方式:
typedef std::vector<int*> vec_type;
typedef std::function<void(make_very_const_t<vec_type::iterator::value_type&>)> func_type;
它以一种应该可以隐式转换为的方式喷出 const
遍及所有内容。
现在,即使这样也不是完全有效。 std::vector< std::vector<int*> >
不会保护指向内部 int
的指针不被修改。以上依赖于隐式转换为 more const
情况的能力——所以要实现这一点,我们必须围绕一个几乎任意的容器编写一个 const-forcing 包装器,强制元素访问通过以上改造。这很难。
一般来说,如果您想要一个 int*
,其中对象 const
指向对象 const
,您需要一个智能指针来执行该要求。 N4388 提议将包装器添加到标准中,即使不完全是为了这个目的。
背景
我正在实现一个模板过滤迭代器。给定任何类型的开始和结束迭代器,此迭代器将迭代范围并跳过一元谓词 returns 为假的任何元素。当然,我希望这个一元谓词始终有一个 const 参数,以避免谓词修改支持容器。
后备迭代器可以是任何类型和容器的迭代器。可以是基本类型,指针,引用,类。真的什么都有。
我 运行 遇到一个问题,我无法根据模板参数迭代器声明 std::function
以具有正确的 const
声明参数。我提炼了一个说明问题的最小代码示例。
代码
#include <vector>
#include <functional>
typedef std::vector<int*> vec_type;
typedef std::function<void(const vec_type::iterator::value_type&)> func_type;
void foo(vec_type& a, func_type f){
for (auto it = a.begin(); it != a.end(); ++it){
f(*it);
}
}
int main(int, char**){
vec_type v;
int a = 3;
int b = 4;
v.push_back(&a);
v.push_back(&b);
foo(v, [](int* x){*x = 0; });
return 0;
}
我预计在 lamda 上会出现编译错误,因为 int*
应该是 const int*
但 GCC 4.8.1 和 VS2013 都允许它并愉快地修改我认为将成为 const 的内容.
您的容器存储 int*
s。您的函数接受 "const reference to int*
"。这意味着指针指向可变数据。数据可以愉快地修改,因为你允许了。请注意 "degrees of constness" 之间的区别 - 您不能更改指针指向的内容,但可以修改它们指向的 int
。因此,要解决此问题,您的函数必须接受 "const reference to const int*
" 或 const int*
.
typedef std::function<void(const int*)> func_type;
...或者,如果您希望它更通用(请参阅 Yakk 的回答以获得更通用的解决方案):
#include <type_traits>
typedef std::vector<int*> vec_type;
typedef
std::add_pointer<
std::add_const<
std::remove_pointer<
vec_type::iterator::value_type
>::type
>::type
>::type value_t;
typedef std::function<void(const value_t&)> func_type;
当您修改指针指向的内容时,指针的容器不会被修改:容器拥有指针,而不是指向的对象。
但我理解 — 有时您希望 const
ness 向下传播指针。
这是一些模板元编程,它应该采用任何非 const
指针并使其成为 const
,以及其他一切都尽可能成为 const
。它以递归方式运行,处理引用(r 和 l 值)和对指针的引用到指向指针的指针的指针,几乎在任何地方都有或没有 const
或 volatile
修饰符。
template<class T>struct tag{using type=T;};
template<class X>
struct make_very_const:tag<const X> {};
template<class X>
using make_very_const_t=typename make_very_const<X>::type;
// makes code below easier to write (namely, the pointer code):
template<class X>
struct make_very_const<const X>:tag<const make_very_const_t<X>> {};
template<class X>
struct make_very_const<volatile X>:tag<const volatile make_very_const_t<X>> {};
template<class X>
struct make_very_const<const volatile X>:tag<const volatile make_very_const_t<X>> {};
// references:
template<class X>
struct make_very_const<X&>:tag<make_very_const_t<X>&>{};
template<class X>
struct make_very_const<X&&>:tag<make_very_const_t<X>&&>{};
// pointers:
template<class X>
struct make_very_const<X*>:tag<make_very_const_t<X>*const>{};
// std::reference_wrapper:
template<class X>
struct make_very_const<std::reference_wrapper<X>>:tag<std::reference_wrapper<make_very_const_t<X>>const>{};
这是一大堆废话。它如此冗长的原因是没有简单的方法来匹配 "type modifiers"(指针、const、volatile、引用等),所以你最终不得不非常具体和冗长。
但它在使用时为您提供了一种干净的方式:
typedef std::vector<int*> vec_type;
typedef std::function<void(make_very_const_t<vec_type::iterator::value_type&>)> func_type;
它以一种应该可以隐式转换为的方式喷出 const
遍及所有内容。
现在,即使这样也不是完全有效。 std::vector< std::vector<int*> >
不会保护指向内部 int
的指针不被修改。以上依赖于隐式转换为 more const
情况的能力——所以要实现这一点,我们必须围绕一个几乎任意的容器编写一个 const-forcing 包装器,强制元素访问通过以上改造。这很难。
一般来说,如果您想要一个 int*
,其中对象 const
指向对象 const
,您需要一个智能指针来执行该要求。 N4388 提议将包装器添加到标准中,即使不完全是为了这个目的。