Initialize std::array with a range (pair of iterators)
With random access iterators, and assuming a certain size at compile-time, you can use a pack of indices to do so:
template <std::size_t... Indices>
struct indices {
using next = indices<Indices..., sizeof...(Indices)>;
};
template <std::size_t N>
struct build_indices {
using type = typename build_indices<N-1>::type::next;
};
template <>
struct build_indices<0> {
using type = indices<>;
};
template <std::size_t N>
using BuildIndices = typename build_indices<N>::type;
template <typename Iterator>
using ValueType = typename std::iterator_traits<Iterator>::value_type;
// internal overload with indices tag
template <std::size_t... I, typename RandomAccessIterator,
typename Array = std::array<ValueType<RandomAccessIterator>, sizeof...(I)>>
Array make_array(RandomAccessIterator first, indices<I...>) {
return Array { { first[I]... } };
}
// externally visible interface
template <std::size_t N, typename RandomAccessIterator>
std::array<ValueType<RandomAccessIterator>, N>
make_array(RandomAccessIterator first, RandomAccessIterator last) {
// last is not relevant if we're assuming the size is N
// I'll assert it is correct anyway
assert(last - first == N);
return make_array(first, BuildIndices<N> {});
}
// usage
auto a = make_array<N>(v.begin(), v.end());
This assumes a compiler capable of eliding the intermediate copies. I think that assumption is not a big stretch.
Actually, it can be done with input iterators as well, since the computation of each element in a braced-init-list is sequenced before the computation of the next element (§8.5.4/4).
// internal overload with indices tag
template <std::size_t... I, typename InputIterator,
typename Array = std::array<ValueType<InputIterator>, sizeof...(I)>>
Array make_array(InputIterator first, indices<I...>) {
return Array { { (void(I), *first++)... } };
}
Since *first++
doesn't have any I
in it, we need a dummy I
to provoke the pack expansion. Comma operator to the rescue, with void()
to silence warnings about lack of effects, and also preventing overloaded commas.
On the initialization of std::array
No.
std::array
is an aggregate, so you get no special functionality like constructors taking iterators. (This actually surprises me, with the introduction of std::initializer_list
I see no harm in making other useful constructors. Perhaps a question is in store.)
This means the only way to use iterators to copy data inside the array is to iterate, and to do that the array
must be already constructed and ready to use.
Initializing std::vector with ranges library
What you’re looking for is
auto b=std::ranges::to<std::vector>(std::ranges::iota_view(0, 5));
Unfortunately, that proposal missed C++20 simply because there wasn’t time to review its wording (after a previous version that added the constructor you tried was found unworkable). Hopefully it’ll be merged—and implemented—early in the C++23 cycle.
How to initialize std::vector from C-style array?
Don't forget that you can treat pointers as iterators:
w_.assign(w, w + len);
Iterating over a part of an array in a range-based loop
Simply adapt the range to be a different type where begin()
and end()
do the right thing.
struct Slice {
int* arr;
size_t n;
int* begin() { return arr; }
int* end() { return arr + n; }
};
for(auto elem : Slice{someArray, 100}) {/*doStuff*/}
How to initialize std::map by an array?
How about this?
const char* s[] = { "car", "sun", "surprise", "asteriks", "alpha", "apple" };
std::map<char, std::set<std::string>> myMap;
for (auto str : s)
myMap[str[0]].insert(str);
// Result: {
// 'a' -> {"alpha", "apple", "asteriks"},
// 'c' -> {"car"},
// 's' -> {"sun", "surprise"}
// }
Range-based for with brace-initializer over non-const values?
You are guessing correctly. std::initializer_list
elements are always const
(which makes sort()
ing them impossible, as sort()
is a non-const
member function) and its elements are always copied (which would make sort()
-ing them meaningless even if they weren't const
). From [dcl.init.list], emphasis mine:
An object of type
std::initializer_list<E>
is constructed from an initializer list as if the implementation
allocated a temporary array of N elements of type const E, where N is the number of elements in the
initializer list. Each element of that array is copy-initialized with the corresponding element of the initializer
list, and thestd::initializer_list<E>
object is constructed to refer to that array. [ Note: A constructor
or conversion function selected for the copy shall be accessible (Clause 11) in the context of the initializer
list. —end note ] If a narrowing conversion is required to initialize any of the elements, the program is
ill-formed. [ Example:
struct X {
X(std::initializer_list<double> v);
};
X x{ 1,2,3 };
The initialization will be implemented in a way roughly equivalent to this:
const double __a[3] = {double{1}, double{2}, double{3}};
X x(std::initializer_list<double>(__a, __a+3));
assuming that the implementation can construct an
initializer_list
object with a pair of pointers. —end
example ]
There is no way to make them non-const or non-copied. The pointer solution works:
for (auto l : {&a, &b, &c}) l->sort();
because it's the pointer that's const, not the element it's pointing to. The other alternative would be to write a variadic function template:
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);
You could also, I guess, write a helper to wrap your lists into an array of reference_wrapper
to list<int>
(since you can't have an array of references), but this is probably more confusing than helpful:
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()
Is one-line initialization of a <set> possible with C++20 <ranges>?
Actually, it is not possible for now, but in near future, we can get ranges::to which will have such functionality.
Related Topics
Getting the Current Time (In Milliseconds) from the System Clock in Windows
How to Edit and Re-Build the Gcc Libstdc++ C++ Standard Library Source
How to Define a Move Constructor
Function Template with an Operator
Overload Resolution and Arrays: Which Function Should Be Called
Portability of Native C++ Properties
C++ Object Instantiation VS Assignment
Using Continue in a Switch Statement
How to Perform a Pairwise Binary Operation Between the Elements of Two Containers
Macro to Replace C++ Operator New
C++ Const Keyword - Use Liberally
How to Use Makefiles in Visual Studio
How to Do Aes Decryption Using Openssl
Google Protocol Buffers on iOS
Can You Use Keyword Explicit to Prevent Automatic Conversion of Method Parameters