eud*_*xos 22 c++ initializer-list c++11
我试图迭代一些std::lists,对它们进行排序.这是天真的方法:
#include<list>
using namespace std;
int main(void){
list<int> a,b,c;
for(auto& l:{a,b,c}) l.sort();
}
Run Code Online (Sandbox Code Playgroud)
生产
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.
Run Code Online (Sandbox Code Playgroud)
我是否正确地猜测大括号初始化程序正在创建这些列表的副本?有没有办法不复制它们,并使它们在循环中可修改?(除了制作指向它们的指针列表,这是我目前的解决方法).
Bar*_*rry 24
你猜对了.std::initializer_list元素总是const(这使得sort()它们变得不可能,因为sort()它是非const成员函数)并且它的元素总是被复制(sort()即使它们不是,它们也会使它们变得毫无意义const).来自[dcl.init.list],强调我的:
类型的对象
std::initializer_list<E>是从初始化列表构造的,就好像实现分配了一个类型为const E的N个元素的临时数组,其中N是初始化列表中的元素数.使用初始化列表的相应元素对该数组的每个元素进行复制初始化,并std::initializer_list<E>构造该对象以引用该数组.[注意:在初始化列表的上下文中,可以访问为副本选择的构造函数或转换函数(第11条).-end note]如果需要缩小转换来初始化任何元素,则程序格式错误.[例如:Run Code Online (Sandbox Code Playgroud)struct X { X(std::initializer_list<double> v); }; X x{ 1,2,3 };初始化将以大致相当于此的方式实现:
Run Code Online (Sandbox Code Playgroud)const double __a[3] = {double{1}, double{2}, double{3}}; X x(std::initializer_list<double>(__a, __a+3));假设实现可以
initializer_list用一对指针构造一个对象. - 末端的例子]
没有办法使它们成为非常量或非复制的.指针解决方案有效:
for (auto l : {&a, &b, &c}) l->sort();
Run Code Online (Sandbox Code Playgroud)
因为它是const 的指针,而不是它指向的元素.另一种选择是编写一个可变参数函数模板:
template <typename... Lists>
void sortAll(Lists&&... lists) {
using expander = int[];
expander{0, (void(lists.sort()), 0)...};
}
sortAll(a, b, c);
Run Code Online (Sandbox Code Playgroud)
我猜你也可以编写一个帮助器来将你的列表包装成一个reference_wrapperto 数组list<int>(因为你不能有一个引用数组),但这可能比有用的更令人困惑:
template <typename List, typename... Lists>
std::array<std::reference_wrapper<List>, sizeof...(Lists) + 1>
as_array(List& x, Lists&... xs) {
return {x, xs...};
}
for (list<int>& l : as_array(a, b, c)) { // can't use auto, that deduces
l.sort(); // reference_wrapper<list<int>>,
} // so would need l.get().sort()
Run Code Online (Sandbox Code Playgroud)
可以编写一个ref_range允许您执行此操作的函数:
for(auto& l : ref_range(a,b,c)) {
l.sort();
}
Run Code Online (Sandbox Code Playgroud)
正如其他人所说的那样,一旦你写下{a,b,c}你就被困在了initializer_list,而这样的列表总是会复制它的论点.副本const(因此您的错误),但即使您可以获得非const引用,您将修改副本a,b而c不是原始副本.
无论如何,这是ref_range.它建立一个vector的reference_wrapper.
// 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();
}
}
Run Code Online (Sandbox Code Playgroud)