C++ 非常量值上带大括号初始值设定项的基于范围的?
我试图迭代许多C++ 非常量值上带大括号初始值设定项的基于范围的?,c++,c++11,initializer-list,C++,C++11,Initializer List,我试图迭代许多std::lists,对它们进行排序。这是一种天真的方法: #include<list> using namespace std; int main(void){ list<int> a,b,c; for(auto& l:{a,b,c}) l.sort(); } #包括 使用名称空间std; 内部主(空){ 清单a、b、c; for(auto&l:{a,b,c})l.sort(); } 生产 aa.cpp:5:25: error:
std::list
s,对它们进行排序。这是一种天真的方法:
#include<list>
using namespace std;
int main(void){
list<int> a,b,c;
for(auto& l:{a,b,c}) l.sort();
}
#包括
使用名称空间std;
内部主(空){
清单a、b、c;
for(auto&l:{a,b,c})l.sort();
}
生产
aa.cpp:5:25: error: no matching member function for call to 'sort'
for(auto& l:{a,b,c}) l.sort();
~~^~~~
/usr/bin/../lib64/gcc/x86_64-linux-gnu/4.9/../../../../include/c++/4.9/bits/stl_list.h:1586:7: note:
candidate function not viable: 'this' argument has type 'const
std::list<int, std::allocator<int> >', but method is not marked const
sort();
^
/usr/bin/../lib64/gcc/x86_64-linux-gnu/4.9/../../../../include/c++/4.9/bits/stl_list.h:1596:9: note:
candidate function template not viable: requires 1 argument, but 0 were
provided
sort(_StrictWeakOrdering);
^
1 error generated.
aa.cpp:5:25:错误:没有用于调用“sort”的匹配成员函数
for(auto&l:{a,b,c})l.sort();
~~^~~~
/usr/bin/./lib64/gcc/x86_64-linux-gnu/4.9/../../../../../../../../../include/c++/4.9/bits/stl_list.h:1586:7:注:
候选函数不可行:“此”参数的类型为“const”
std::list',但方法未标记为const
排序();
^
/usr/bin/./lib64/gcc/x86_64-linux-gnu/4.9/../../../../../../../../../../include/c++/4.9/bits/stl_list.h:1596:9:注:
候选函数模板不可行:需要1个参数,但0不可用
假如
排序(严格排序);
^
生成1个错误。
我是否正确地猜测大括号初始值设定项正在创建这些列表的副本?有没有办法不复制它们,并使它们在循环中可修改?(除了列出指向它们的指针之外,这是我目前的解决方法)。你猜对了
std::initializer\u list
元素总是const
(这使得sort()
不可能对它们进行排序,因为sort()
是一个非const
成员函数)并且它的元素总是被复制的(这将使得sort()
-即使它们不是const
也没有意义)。从[dcl.init.list]中,强调:
类型为std::initializer\u list
的对象是从初始值设定项列表构造的,就像实现
分配了一个由N个元素组成的临时数组,类型为常量,其中N是数组中的元素数
初始化列表。该数组的每个元素都是用初始值设定项的相应元素进行复制初始化的
列表,然后构造std::initializer\u list
对象以引用该数组。[注:构造函数
或为副本选择的转换功能应在初始值设定项上下文中可访问(第11条)
列表.-结束注释]如果初始化任何元素需要进行缩小转换,则程序为
格式不正确。[示例:
因为它是常量的指针,而不是它所指向的元素。另一种选择是编写可变函数模板:
template <typename... Lists>
void sortAll(Lists&&... lists) {
// before C++17
using expander = int[];
expander{0, (void(lists.sort()), 0)...};
// C++17 or later
(lists.sort(), ...);
}
sortAll(a, b, c);
{…}
语法实际上正在创建一个。如链接页面所述:
在以下情况下,将自动构造std::initializer\u list
对象:
- [……]
- 大括号的init列表绑定到
,包括在ranged for循环中auto
std::initializer\u list
类型的对象是一个轻量级代理对象,它提供对const T
类型的对象数组的访问
因此,您无法修改通过此
初始化\u列表访问的对象。您的带有指针的解决方案在我看来是最简单的解决方案。直接回答您的问题:
我猜对了吗?括号初始值设定项正在创建
那些名单
是的,这是第一个问题。您的代码将创建列表的副本,对这些副本进行排序,最后忘记已排序的副本
但是,仅此一点就只会导致代码无法工作。编译器错误提示了第二个问题:l
的隐式类型是list const&
,而不是list&
。因此编译器抱怨sort()
试图修改常量列表
您可以使用讨厌的常量来解决第二个问题:
#include <list>
#include <iostream>
using namespace std;
int main(void){
list<int> a,b,c;
a.push_back(2);
a.push_back(0);
a.push_back(1);
for(auto& l:{a,b,c}) const_cast<list<int>&>(l).sort();
for(auto i:a) cout << i << endl;
}
最简单的解决方法是创建指向列表的指针列表:
#include <list>
#include <iostream>
using namespace std;
int main(void){
list<int> a,b,c;
a.push_back(2);
a.push_back(0);
a.push_back(1);
for(auto l:{&a,&b,&c}) l->sort();
for(auto i:a) cout << i << endl;
}
可以编写一个函数ref\u range
,它允许您执行以下操作:
for(auto& l : ref_range(a,b,c)) {
l.sort();
}
正如其他人所说,一旦你写了{a,b,c}
,你就会被困在一个初始值设定项列表中,这样的列表总是复制它的参数。副本是const
(这就是你的错误),但即使您可以获得非常量
引用,您也将修改a
、b
和c
的副本,而不是原件
无论如何,这里是ref\u range
。它构建了reference\u包装的向量
// http://stackoverflow.com/questions/31724863/range-based-for-with-brace-initializer-over-non-const-values
#include<list>
#include<functional>
#include<array>
template<typename T, std:: size_t N>
struct hold_array_of_refs {
using vec_type = std:: array< std:: reference_wrapper<T>, N >;
vec_type m_v_of_refs;
hold_array_of_refs(vec_type && v_of_refs) : m_v_of_refs(std::move(v_of_refs)) { }
~hold_array_of_refs() { }
struct iterator {
typename vec_type :: const_iterator m_it;
iterator(typename vec_type :: const_iterator it) : m_it(it) {}
bool operator != (const iterator &other) {
return this->m_it != other.m_it;
}
iterator& operator++() { // prefix
++ this->m_it;
return *this;
}
T& operator*() {
return *m_it;
}
};
iterator begin() const {
return iterator(m_v_of_refs.begin());
}
iterator end() const {
return iterator(m_v_of_refs.end());
}
};
template<typename... Ts>
using getFirstTypeOfPack = typename std::tuple_element<0, std::tuple<Ts...>>::type;
template<typename ...T>
auto ref_range(T&... args) -> hold_array_of_refs< getFirstTypeOfPack<T...> , sizeof...(args)> {
return {{{ std:: ref(args)... }}}; // Why does clang prefer three levels of {} ?
}
#include<iostream>
int main(void){
std:: list<int> a,b,c;
// print the addresses, so we can verify we're dealing
// with the same objects
std:: cout << &a << std:: endl;
std:: cout << &b << std:: endl;
std:: cout << &c << std:: endl;
for(auto& l : ref_range(a,b,c)) {
std:: cout << &l << std:: endl;
l.sort();
}
}
//http://stackoverflow.com/questions/31724863/range-based-for-with-brace-initializer-over-non-const-values
#包括
#包括
#包括
模板
结构保持\u数组\u的\u引用{
使用vec_type=std::array;
vec_类型m_v_的参考;
hold_数组_of_refs(vec_类型和&v_of_refs):m_v_of_refs(std::move(v_of_refs)){
~hold_数组_of_refs(){}
结构迭代器{
typename vec_type::const_迭代器m_it;
迭代器(typename vec_type::const_iterator it):m_it(it){}
布尔运算符!=(常量迭代器和其他){
返回此->m_it!=其他m_it;
}
迭代器和运算符++(){//前缀
++这个->我喜欢它;
归还*这个;
}
T&运算符*(){
返回*m_it;
}
};
迭代器begin()常量{
返回迭代器(m_v_of_refs.begin());
}
迭代器end()常量{
返回迭代器(m_v_of_refs.end());
}
};
模板
使用getFirstTypeOfPack=typename std::tuple_元素::type;
模板
自动引用范围(T&…args)->保留引用的数组{
返回{{{std::ref(args)…}}};//为什么clang更喜欢三级{}?
}
#包括
内部主(空){
标准:列表a、b、c;
//打印地址,这样我们就可以确认我们在交易
//使用相同的对象
std::cout其他人已经提到了std::reference\u wrapper
,但他们随后使用它来创建STL容器,而不是使用
#include <list>
#include <iostream>
using namespace std;
int main(void){
list<int> a,b,c;
a.push_back(2);
a.push_back(0);
a.push_back(1);
for(auto& l:{a,b,c}) const_cast<list<int>&>(l).sort();
for(auto i:a) cout << i << endl;
}
2
0
1
#include <list>
#include <iostream>
using namespace std;
int main(void){
list<int> a,b,c;
a.push_back(2);
a.push_back(0);
a.push_back(1);
for(auto l:{&a,&b,&c}) l->sort();
for(auto i:a) cout << i << endl;
}
0
1
2
for(auto& l : ref_range(a,b,c)) {
l.sort();
}
// http://stackoverflow.com/questions/31724863/range-based-for-with-brace-initializer-over-non-const-values
#include<list>
#include<functional>
#include<array>
template<typename T, std:: size_t N>
struct hold_array_of_refs {
using vec_type = std:: array< std:: reference_wrapper<T>, N >;
vec_type m_v_of_refs;
hold_array_of_refs(vec_type && v_of_refs) : m_v_of_refs(std::move(v_of_refs)) { }
~hold_array_of_refs() { }
struct iterator {
typename vec_type :: const_iterator m_it;
iterator(typename vec_type :: const_iterator it) : m_it(it) {}
bool operator != (const iterator &other) {
return this->m_it != other.m_it;
}
iterator& operator++() { // prefix
++ this->m_it;
return *this;
}
T& operator*() {
return *m_it;
}
};
iterator begin() const {
return iterator(m_v_of_refs.begin());
}
iterator end() const {
return iterator(m_v_of_refs.end());
}
};
template<typename... Ts>
using getFirstTypeOfPack = typename std::tuple_element<0, std::tuple<Ts...>>::type;
template<typename ...T>
auto ref_range(T&... args) -> hold_array_of_refs< getFirstTypeOfPack<T...> , sizeof...(args)> {
return {{{ std:: ref(args)... }}}; // Why does clang prefer three levels of {} ?
}
#include<iostream>
int main(void){
std:: list<int> a,b,c;
// print the addresses, so we can verify we're dealing
// with the same objects
std:: cout << &a << std:: endl;
std:: cout << &b << std:: endl;
std:: cout << &c << std:: endl;
for(auto& l : ref_range(a,b,c)) {
std:: cout << &l << std:: endl;
l.sort();
}
}
for(auto& l:{std::ref(a),std::ref(b),std::ref(c)}) l.get().sort();