C++ 如何正确地将常量指针从非常量迭代器声明为指针 背景
我正在实现一个模板过滤迭代器。给定任何类型的开始和结束迭代器,该迭代器将在范围内迭代,并跳过一元谓词返回false的任何元素。当然,我希望这个一元谓词总是有一个const参数,以避免谓词修改支持容器 支持迭代器可以是任何类型和容器的迭代器。它可以是基本类型、指针、引用、类。什么都可以 我遇到了一个问题,无法根据模板参数迭代器声明C++ 如何正确地将常量指针从非常量迭代器声明为指针 背景,c++,c++11,const-correctness,C++,C++11,Const Correctness,我正在实现一个模板过滤迭代器。给定任何类型的开始和结束迭代器,该迭代器将在范围内迭代,并跳过一元谓词返回false的任何元素。当然,我希望这个一元谓词总是有一个const参数,以避免谓词修改支持容器 支持迭代器可以是任何类型和容器的迭代器。它可以是基本类型、指针、引用、类。什么都可以 我遇到了一个问题,无法根据模板参数迭代器声明std::function以获得正确的const声明参数。我提取了一个说明问题的最小代码示例 代码 #包括 #包括 typedef std::向量向量类型; typede
std::function
以获得正确的const
声明参数。我提取了一个说明问题的最小代码示例
代码
#包括
#包括
typedef std::向量向量类型;
typedef std::函数函数函数类型;
void foo(向量类型&a,函数类型f){
对于(自动it=a.begin();it!=a.end();++it){
f(*it);
}
}
int main(int,char**){
v型vec_;
INTA=3;
int b=4;
v、 向后推(&a);
v、 向后推(&b);
foo(v,[](int*x){*x=0;});
返回0;
}
我希望lamda上会出现编译错误,因为
int*
应该是const int*
,但GCC 4.8.1和VS2013都允许它,并且很高兴地修改了我认为应该是const的内容。您的容器存储int*
s。您的函数接受“对int*
的常量引用”。这意味着指针指向可变数据。由于您允许修改数据,因此可以很高兴地对其进行修改。注意“恒定度”之间的区别——您不能更改指针指向的对象,但可以修改它们指向的int
s。因此,要解决此问题,您的函数必须接受“const reference toconst int*
”或const int*
typedef std::function<void(const int*)> func_type;
typedef std::function func_type;
…或者,如果您希望它更通用(请参阅雅克的答案,了解更通用的解决方案):
#包括
typedef std::向量向量类型;
类型定义
std::add_指针<
标准::添加常数<
删除指针<
向量类型::迭代器::值类型
>::类型
>::类型
>::类型值\u t;
typedef std::函数函数函数类型;
修改指针指向的对象时,指针容器不会被修改:容器拥有指针,而不是指向的对象
但我明白——有时候你想要const
ness来分配下指针
这是一个模板元编程,它应该采用任何非常量
指针,并使其成为常量
,以及其他所有内容尽可能成为常量。它以递归方式运行,处理引用(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>{};
templatestruct标记{using type=T;};
模板
结构make_very_const:tag{};
模板
使用make_very_const_t=typename make_very_const::type;
//使下面的代码更易于编写(即指针代码):
模板
结构make_very_const:tag{};
模板
结构make_very_const:tag{};
模板
结构make_very_const:tag{};
//参考资料:
模板
结构make_very_const:tag{};
模板
结构make_very_const:tag{};
//指针:
模板
结构make_very_const:tag{};
//标准::参考包装:
模板
结构make_very_const:tag{};
这是非常冗长的。之所以如此冗长,是因为没有简单的方法来匹配“类型修饰符”(指针、常量、volatile、引用等),所以最终必须非常具体和冗长
但它为您在使用时提供了一种干净的方法:
typedef std::vector<int*> vec_type;
typedef std::function<void(make_very_const_t<vec_type::iterator::value_type&>)> func_type;
typedef标准::向量向量类型;
typedef std::函数函数函数类型;
它以一种应该隐式转换为的方式将const
喷到所有东西上
现在,即使这样也不是完全有效。std::vector
不会保护指向内部的int
不被修改。以上依赖于隐式转换为更const
的能力——因此要实现这一点,我们必须在几乎任意的容器周围编写一个const-forcing包装器,强制元素访问以完成上述转换。这很难
通常,如果您想要一个int*
对象,其中const
使指向对象const
,则需要一个智能指针来强制执行该要求。建议在标准中添加一个这样做的包装器,如果不完全出于此目的。使用vec_type::const_iterator
而不是const-vec_type::iterator
@WojciechFrohmberg,这在我的情况下是不可接受的。我将添加一些背景信息。func\u type
的参数类型是“const reference toint*
”,而不是“reference toconst int*
”。如果将调用更改为foo(v,[](int*&x){x=0;})代码>它会失败。@molbdnilo我怀疑发生了类似的事情。但是,如果用户忘记添加&
(如果您不阅读文档,这一点就不明显),那么代码将很高兴地修改备份容器。你对如何解决这个问题有什么建议吗?[](int*x){x=0;}
-你为什么认为这是在修改容器?你的答案正是我需要的。我只是想了解你的模板(我用了不同的技术,但结果是一样的)。@EmilyLtag
是我的懒惰(我可以用{using type=blah;}
替换:tag{}
。其他一切都只是“匹配模式,做任何事情”pattern.make\u very\u const\t
是一个别名,它消除了typename
的无稽之谈。这些模式基本上是只添加const
不正确的情况:排列得不正确
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>{};
typedef std::vector<int*> vec_type;
typedef std::function<void(make_very_const_t<vec_type::iterator::value_type&>)> func_type;