Vc  1.1.0
SIMD Vector Classes for C++
subscript.h
1 /* This file is part of the Vc library. {{{
2 Copyright © 2013-2015 Matthias Kretz <kretz@kde.org>
3 All rights reserved.
4 
5 Redistribution and use in source and binary forms, with or without
6 modification, are permitted provided that the following conditions are met:
7  * Redistributions of source code must retain the above copyright
8  notice, this list of conditions and the following disclaimer.
9  * Redistributions in binary form must reproduce the above copyright
10  notice, this list of conditions and the following disclaimer in the
11  documentation and/or other materials provided with the distribution.
12  * Neither the names of contributing organizations nor the
13  names of its contributors may be used to endorse or promote products
14  derived from this software without specific prior written permission.
15 
16 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
17 ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
18 WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
19 DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE FOR ANY
20 DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
21 (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
22 LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
23 ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
24 (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
25 SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26 
27 }}}*/
28 
29 #ifndef VC_COMMON_SUBSCRIPT_H_
30 #define VC_COMMON_SUBSCRIPT_H_
31 
32 #include <initializer_list>
33 #include <ratio>
34 #include <type_traits>
35 #include <vector>
36 #include "types.h"
37 #include "macros.h"
38 #include <assert.h>
39 
40 namespace Vc_VERSIONED_NAMESPACE
41 {
42 namespace Common
43 {
44 // AdaptSubscriptOperator {{{1
45 template <typename Base> class AdaptSubscriptOperator : public Base
46 {
47 public:
48  // perfect forward all Base constructors
49  template <typename... Args>
50  Vc_ALWAYS_INLINE AdaptSubscriptOperator(Args &&... arguments)
51  : Base(std::forward<Args>(arguments)...)
52  {
53  }
54 
55  // perfect forward all Base constructors
56  template <typename T>
57  Vc_ALWAYS_INLINE AdaptSubscriptOperator(std::initializer_list<T> l)
58  : Base(l)
59  {
60  }
61 
62  // explicitly enable Base::operator[] because the following would hide it
63  using Base::operator[];
64 
66  template <typename I,
67  typename = enable_if<!std::is_arithmetic<
68  typename std::decay<I>::type>::value> // arithmetic types
69  // should always use
70  // Base::operator[] and
71  // never match this one
72  >
73  Vc_ALWAYS_INLINE auto operator[](I &&arg_)
74  -> decltype(subscript_operator(*this, std::forward<I>(arg_)))
75  {
76  return subscript_operator(*this, std::forward<I>(arg_));
77  }
78 
79  // const overload of the above
80  template <typename I, typename = enable_if<
81  !std::is_arithmetic<typename std::decay<I>::type>::value>>
82  Vc_ALWAYS_INLINE auto operator[](I &&arg_) const
83  -> decltype(subscript_operator(*this, std::forward<I>(arg_)))
84  {
85  return subscript_operator(*this, std::forward<I>(arg_));
86  }
87 };
88 // apply Scale (std::ratio) functions {{{1
89 template <typename Scale, typename T>
90 Vc_ALWAYS_INLINE enable_if<Scale::num == Scale::den, Traits::decay<T>> applyScale(T &&x)
91 {
92  return std::forward<T>(x);
93 }
94 
95 template <typename Scale, typename T>
96 Vc_ALWAYS_INLINE enable_if<
97  Scale::num != Scale::den && Traits::has_multiply_operator<T, std::intmax_t>::value,
98  Traits::decay<T>>
99  applyScale(T &&x)
100 {
101  static_assert(Scale::num % Scale::den == 0,
102  "Non-integral index scaling requested. This typically happens only for "
103  "Vc::Scalar on 32-bit for gathers on double. You can work around the "
104  "issue by ensuring that all doubles in the structure are aligned on 8 "
105  "Bytes.");
106  constexpr auto value = Scale::num / Scale::den;
107  Vc_ASSERT(Vc::all_of((x * value) / value == x));
108  return std::forward<T>(x) * value;
109 }
110 
111 template <typename Scale, typename T>
112 Vc_ALWAYS_INLINE enable_if<
113  Scale::num != Scale::den && !Traits::has_multiply_operator<T, std::intmax_t>::value,
114  T>
115  applyScale(T x)
116 {
117  static_assert(Scale::num % Scale::den == 0,
118  "Non-integral index scaling requested. This typically happens only for "
119  "Vc::Scalar on 32-bit for gathers on double. You can work around the "
120  "issue by ensuring that all doubles in the structure are aligned on 8 "
121  "Bytes.");
122  constexpr auto value = Scale::num / Scale::den;
123  for (size_t i = 0; i < x.size(); ++i) {
124  Vc_ASSERT((x[i] * value) / value == x[i]);
125  x[i] *= value;
126  }
127  return x;
128 }
129 
130 template <typename Scale, typename T, typename U,
131  typename = enable_if<Traits::has_multiply_operator<T, std::intmax_t>::value &&
132  Traits::has_addition_operator<T, U>::value>>
133 Vc_ALWAYS_INLINE typename std::decay<T>::type applyScaleAndAdd(T &&x, U &&y)
134 {
135  constexpr auto value = Scale::num / Scale::den;
136  if (value == 1) { // static evaluation
137  return std::forward<T>(x) + std::forward<U>(y);
138  }
139  return std::forward<T>(x) * value + std::forward<U>(y);
140 }
141 
142 template <
143  typename Scale, typename T, typename U,
144  typename = enable_if<
145  !(Traits::has_multiply_operator<T &, std::intmax_t>::value &&
146  Traits::has_addition_operator<T &, decltype(std::declval<U>()[0])>::value) &&
147  Traits::has_subscript_operator<U>::value>>
148 Vc_ALWAYS_INLINE T applyScaleAndAdd(T x, U &&y)
149 {
150  constexpr auto value = Scale::num / Scale::den;
151  for (size_t i = 0; i < x.size(); ++i) {
152  if (value == 1) { // static evaluation
153  x[i] = x[i] + y[i];
154  } else {
155  x[i] = x[i] * value + y[i];
156  }
157  }
158  return x;
159 }
160 
161 template <typename Scale, typename T, typename U>
162 Vc_ALWAYS_INLINE enable_if<!(Traits::has_multiply_operator<T &, std::intmax_t>::value &&
163  Traits::has_addition_operator<T &, U>::value) &&
164  !Traits::has_subscript_operator<U>::value,
165  T>
166  applyScaleAndAdd(T x, U &&y)
167 {
168  constexpr auto value = Scale::num / Scale::den;
169  for (size_t i = 0; i < x.size(); ++i) {
170  if (value == 1) { // static evaluation
171  x[i] = x[i] + y;
172  } else {
173  x[i] = x[i] * value + y;
174  }
175  }
176  return x;
177 }
178 
179 // IndexVectorSizeMatches {{{1
180 template <std::size_t MinSize,
181  typename IndexT,
182  bool = Traits::is_simd_vector<IndexT>::value>
183 struct IndexVectorSizeMatches
184  : public std::true_type // you might expect this should be false_type here, but the point is
185  // that IndexT is a type where the size is not known at compile time.
186  // Thus it may be good but we cannot know from the type. The only check
187  // we could do is a runtime check, but the type is fine.
188 {
189 };
190 
191 template <std::size_t MinSize, typename V>
192 struct IndexVectorSizeMatches<MinSize,
193  V,
194  true> : public std::integral_constant<bool, (MinSize <= V::Size)>
195 {
196 };
197 
198 template <std::size_t MinSize, typename T, std::size_t ArraySize>
199 struct IndexVectorSizeMatches<MinSize,
200  T[ArraySize],
201  false> : public std::integral_constant<bool, (MinSize <= ArraySize)>
202 {
203 };
204 
205 template <std::size_t MinSize, typename T, std::size_t ArraySize>
206 struct IndexVectorSizeMatches<MinSize,
207  std::array<T, ArraySize>,
208  false> : public std::integral_constant<bool, (MinSize <= ArraySize)>
209 {
210 };
211 
212 template <std::size_t MinSize, typename T, std::size_t ArraySize>
213 struct IndexVectorSizeMatches<MinSize,
214  Vc::array<T, ArraySize>,
215  false> : public std::integral_constant<bool, (MinSize <= ArraySize)>
216 {
217 };
218 // convertIndexVector {{{1
219 // if the argument is a Vector<T> already we definitely want to keep it that way
220 template <typename IV>
221 enable_if<
222  (Traits::is_simd_vector<IV>::value && sizeof(typename IV::EntryType) >= sizeof(int)),
223  const IV &>
224  convertIndexVector(const IV &indexVector)
225 {
226  return indexVector;
227 }
228 
229 // but if the scalar (integral) type is smaller than int we convert it up to int. Otherwise it's
230 // very likely that the calculations we have to perform will overflow.
231 template <typename IV>
232 enable_if<
233  (Traits::is_simd_vector<IV>::value && sizeof(typename IV::EntryType) < sizeof(int)),
234  SimdArray<int, IV::Size>>
235  convertIndexVector(const IV &indexVector)
236 {
237  return static_cast<SimdArray<int, IV::Size>>(indexVector);
238 }
239 
240 // helper for promoting int types to int or higher
241 template<typename T> using promoted_type = decltype(std::declval<T>() + 1);
242 
243 // std::array, Vc::array, and C-array are fixed size and can therefore be converted to a
244 // SimdArray of the same size
245 template <typename T, std::size_t N>
246 enable_if<std::is_integral<T>::value, SimdArray<promoted_type<T>, N>> convertIndexVector(
247  const std::array<T, N> &indexVector)
248 {
249  return {std::addressof(indexVector[0]), Vc::Unaligned};
250 }
251 template <typename T, std::size_t N>
252 enable_if<std::is_integral<T>::value, SimdArray<promoted_type<T>, N>> convertIndexVector(
253  const Vc::array<T, N> &indexVector)
254 {
255  return {std::addressof(indexVector[0]), Vc::Unaligned};
256 }
257 template <typename T, std::size_t N>
258 enable_if<std::is_integral<T>::value, SimdArray<promoted_type<T>, N>> convertIndexVector(
259  const T (&indexVector)[N])
260 {
261  return SimdArray<promoted_type<T>, N>{std::addressof(indexVector[0]), Vc::Unaligned};
262 }
263 
264 // a plain pointer won't work. Because we need some information on the number of values in
265 // the index argument
266 template <typename T>
267 enable_if<std::is_pointer<T>::value, void> convertIndexVector(T indexVector) = delete;
268 
269 // an initializer_list works, but is runtime-sized (before C++14, at least) so we have to
270 // fall back to std::vector
271 template <typename T>
272 std::vector<promoted_type<T>> convertIndexVector(
273  const std::initializer_list<T> &indexVector)
274 {
275  return {begin(indexVector), end(indexVector)};
276 }
277 
278 // a std::vector cannot be converted to anything better
279 template <typename T>
280 enable_if<(std::is_integral<T>::value && sizeof(T) >= sizeof(int)), std::vector<T>>
281  convertIndexVector(const std::vector<T> &indexVector)
282 {
283  return indexVector;
284 }
285 template <typename T>
286 enable_if<(std::is_integral<T>::value && sizeof(T) < sizeof(int)),
287  std::vector<promoted_type<T>>>
288  convertIndexVector(const std::vector<T> &indexVector)
289 {
290  return {std::begin(indexVector), std::end(indexVector)};
291 }
292 
293 template <typename T, typename = decltype(convertIndexVector(std::declval<T>()))>
294 std::true_type is_valid_indexvector(T &&);
295 std::false_type is_valid_indexvector(...);
296 
297 // SubscriptOperation {{{1
298 template <
299  typename T, typename IndexVector, typename Scale = std::ratio<1, 1>,
300  bool = decltype(is_valid_indexvector(std::declval<const IndexVector &>()))::value>
301 class SubscriptOperation
302 {
303  const IndexVector m_indexes;
304  T *const m_address;
305  using ScalarType = typename std::decay<T>::type;
306 
307  using IndexVectorScaled = Traits::decay<decltype(convertIndexVector(std::declval<const IndexVector &>()))>;
308 
309 public:
310  template <typename U,
311  typename = enable_if<((std::is_convertible<const U &, IndexVector>::value ||
312  std::is_same<U, IndexVector>::value) &&
313  std::is_copy_constructible<IndexVector>::value)>>
314  constexpr Vc_ALWAYS_INLINE SubscriptOperation(T *address, const U &indexes)
315  : m_indexes(indexes), m_address(address)
316  {
317  }
318 
319  template <std::size_t... Indexes>
320  constexpr Vc_ALWAYS_INLINE SubscriptOperation(T *address, const IndexVector &indexes,
321  index_sequence<Indexes...>)
322  : m_indexes{indexes[Indexes]...}, m_address(address)
323  {}
324 
325  template <typename U>
326  constexpr Vc_ALWAYS_INLINE SubscriptOperation(
327  T *address, const U &indexes,
328  enable_if<((std::is_convertible<const U &, IndexVector>::value ||
329  std::is_same<U, IndexVector>::value) &&
330  !std::is_copy_constructible<IndexVector>::value &&
331  std::is_array<IndexVector>::value &&
332  std::extent<IndexVector>::value > 0)> = nullarg)
333  : SubscriptOperation(address, indexes,
334  make_index_sequence<std::extent<IndexVector>::value>())
335  {
336  }
337 
338  Vc_ALWAYS_INLINE GatherArguments<T, IndexVectorScaled> gatherArguments() const
339  {
340  static_assert(std::is_arithmetic<ScalarType>::value,
341  "Incorrect type for a SIMD vector gather. Must be an arithmetic type.");
342  return {applyScale<Scale>(convertIndexVector(m_indexes)), m_address};
343  }
344 
345  Vc_ALWAYS_INLINE ScatterArguments<T, IndexVectorScaled> scatterArguments() const
346  {
347  static_assert(std::is_arithmetic<ScalarType>::value,
348  "Incorrect type for a SIMD vector scatter. Must be an arithmetic type.");
349  return {applyScale<Scale>(convertIndexVector(m_indexes)), m_address};
350  }
351 
352  template <typename V,
353  typename = enable_if<(std::is_arithmetic<ScalarType>::value &&Traits::is_simd_vector<
354  V>::value &&IndexVectorSizeMatches<V::Size, IndexVector>::value)>>
355  Vc_ALWAYS_INLINE operator V() const
356  {
357  static_assert(std::is_arithmetic<ScalarType>::value,
358  "Incorrect type for a SIMD vector gather. Must be an arithmetic type.");
359  const auto indexes = applyScale<Scale>(convertIndexVector(m_indexes));
360  return V(m_address, indexes);
361  }
362 
363  template <typename V,
364  typename = enable_if<(std::is_arithmetic<ScalarType>::value &&Traits::is_simd_vector<
365  V>::value &&IndexVectorSizeMatches<V::Size, IndexVector>::value)>>
366  Vc_ALWAYS_INLINE SubscriptOperation &operator=(const V &rhs)
367  {
368  static_assert(std::is_arithmetic<ScalarType>::value,
369  "Incorrect type for a SIMD vector scatter. Must be an arithmetic type.");
370  const auto indexes = applyScale<Scale>(convertIndexVector(m_indexes));
371  rhs.scatter(m_address, indexes);
372  return *this;
373  }
374 
375  // precondition: m_address points to a struct/class/union
376  template <
377  typename U,
378  typename S, // S must be equal to T. Still we require this template parameter -
379  // otherwise instantiation of SubscriptOperation would only be valid for
380  // structs/unions.
381  typename = enable_if<std::is_same<S, typename std::remove_cv<T>::type>::value &&(
382  std::is_class<T>::value || std::is_union<T>::value)>>
383  Vc_ALWAYS_INLINE auto operator[](U S::*member)
384  -> SubscriptOperation<
385  typename std::conditional<std::is_const<T>::value,
386  const typename std::remove_reference<U>::type,
387  typename std::remove_reference<U>::type>::type,
388  IndexVector,
389  // By passing the scale factor as a fraction of integers in the template
390  // arguments the value does not lose information if the division yields a
391  // non-integral value. This could happen e.g. for a struct of struct (S2 {
392  // S1, char }, with sizeof(S1) = 16, sizeof(S2) = 20. Then scale would be
393  // 20/16)
394  std::ratio_multiply<Scale, std::ratio<sizeof(S), sizeof(U)>>>
395  {
396  static_assert(std::is_same<Traits::decay<decltype(m_address->*member)>,
397  Traits::decay<U>>::value,
398  "Type mismatch that should be impossible.");
399  // TODO: check whether scale really works for unions correctly
400  return {&(m_address->*member), m_indexes};
401  }
402 
403  /*
404  * The following functions allow subscripting of nested arrays. But
405  * there are two cases of containers and only one that we want to support:
406  * 1. actual arrays (e.g. T[N] or std::array<T, N>)
407  * 2. dynamically allocated vectors (e.g. std::vector<T>)
408  *
409  * For (1.) the offset calculation is straightforward.
410  * For (2.) the m_address pointer points to memory where pointers are
411  * stored to the actual data. Meaning the data can be scattered
412  * freely in memory (and far away from what m_address points to). Supporting this leads to
413  * serious trouble with the pointer (it does not really point to the start of a memory
414  * region anymore) and inefficient code. The user is better off to write a loop that assigns the
415  * scalars to the vector object sequentially.
416  */
417 
418  // precondition: m_address points to a type that implements the subscript operator
419  template <
420  typename U =
421  T> // U is only required to delay name lookup to the 2nd phase (on use).
422  // This is necessary because m_address[0][index] is only a correct
423  // expression if has_subscript_operator<T>::value is true.
424  Vc_ALWAYS_INLINE auto
425  operator[](enable_if<
426 #ifndef Vc_IMPROVE_ERROR_MESSAGES
427  Traits::has_no_allocated_data<T>::value &&
428  Traits::has_subscript_operator<T>::value &&
429 #endif
430  std::is_same<T, U>::value,
431  std::size_t> index)
432  -> SubscriptOperation<
433  // the following decltype expression must depend on index and cannot
434  // simply use [0][0] because it would yield an invalid expression in
435  // case m_address[0] returns a struct/union
436  typename std::remove_reference<decltype(m_address[0][index])>::type,
437  IndexVector,
438  std::ratio_multiply<Scale,
439  std::ratio<sizeof(T), sizeof(m_address[0][index])>>>
440  {
441  static_assert(Traits::has_subscript_operator<T>::value,
442  "The subscript operator was called on a type that does not implement it.\n");
443  static_assert(Traits::has_no_allocated_data<T>::value,
444  "Invalid container type in gather/scatter operation.\nYou may only use "
445  "nested containers that store the data inside the object (such as builtin "
446  "arrays or std::array) but not containers that store data in allocated "
447  "memory (such as std::vector).\nSince this feature cannot be queried "
448  "generically at compile time you need to spezialize the "
449  "Vc::Traits::has_no_allocated_data_impl<T> type-trait for custom types that "
450  "meet the requirements.\n");
451  static_assert(std::is_lvalue_reference<decltype(m_address[0][index])>::value,
452  "The container does not return an lvalue reference to the data at "
453  "the requested offset. This makes it impossible to execute a "
454  "gather operation.\n");
455  return {&(m_address[0][index]), m_indexes};
456  }
457 
458  // precondition: m_address points to a type that implements the subscript operator
459  template <typename IT>
460  Vc_ALWAYS_INLINE enable_if<
461 #ifndef Vc_IMPROVE_ERROR_MESSAGES
462  Traits::has_no_allocated_data<T>::value &&Traits::has_subscript_operator<T>::value &&
463 #endif
464  Traits::has_subscript_operator<IT>::value,
465  SubscriptOperation<
466  typename std::remove_reference<
467  decltype(m_address[0][std::declval<const IT &>()[0]] // std::declval<IT>()[0] could
468  // be replaced with 0 if it
469  // were not for two-phase lookup. We need to make the
470  // m_address[0][0] expression dependent on IT
471  )>::type,
472  IndexVectorScaled,
473  std::ratio<1, 1> // reset Scale to 1 since it is applied below
474  >>
475  operator[](const IT &index)
476  {
477  static_assert(Traits::has_subscript_operator<T>::value,
478  "The subscript operator was called on a type that does not implement it.\n");
479  static_assert(Traits::has_no_allocated_data<T>::value,
480  "Invalid container type in gather/scatter operation.\nYou may only use "
481  "nested containers that store the data inside the object (such as builtin "
482  "arrays or std::array) but not containers that store data in allocated "
483  "memory (such as std::vector).\nSince this feature cannot be queried "
484  "generically at compile time you need to spezialize the "
485  "Vc::Traits::has_no_allocated_data_impl<T> type-trait for custom types that "
486  "meet the requirements.\n");
487  return {&(m_address[0][0]),
488  applyScaleAndAdd<std::ratio_multiply<
489  Scale, std::ratio<sizeof(T), sizeof(m_address[0][0])>>>(
490  convertIndexVector(m_indexes), index)};
491  }
492 };
493 
494 // specialization for invalid IndexVector type
495 template <typename T, typename IndexVector, typename Scale>
496 class SubscriptOperation<T, IndexVector, Scale, false>;
497 
498 // subscript_operator {{{1
499 template <
500  typename Container,
501  typename IndexVector,
502  typename = enable_if<
503  Traits::has_subscript_operator<IndexVector>::value // The index vector must provide [] for
504  // the implementations of gather/scatter
505  &&Traits::has_contiguous_storage<Container>::value // Container must use contiguous
506  // storage, otherwise the index vector
507  // cannot be used as memory offsets, which is required for efficient
508  // gather/scatter implementations
509  &&std::is_lvalue_reference<decltype(*begin(std::declval<
510  Container>()))>::value // dereferencing the begin iterator must yield an lvalue
511  // reference (const or non-const). Otherwise it is not possible
512  // to determine a pointer to the data storage (see above).
513  >>
514 Vc_ALWAYS_INLINE SubscriptOperation<
515  typename std::remove_reference<decltype(*begin(std::declval<Container>()))>::
516  type, // the type of the first value in the container is what the internal array pointer
517  // has to point to. But if the subscript operator of the container returns a
518  // reference we need to drop that part because it's useless information for us. But
519  // const and volatile, as well as array rank/extent are interesting and need not be
520  // dropped.
521  typename std::remove_const<typename std::remove_reference<
522  IndexVector>::type>::type // keep volatile and possibly the array extent, but the const and
523  // & parts of the type need to be removed because
524  // SubscriptOperation explicitly adds them for its member type
525  > subscript_operator(Container &&c, IndexVector &&indexes)
526 {
527  Vc_ASSERT(std::addressof(*begin(c)) + 1 ==
528  std::addressof(*(begin(c) + 1))); // runtime assertion for contiguous storage, this
529  // requires a RandomAccessIterator - but that
530  // should be given for a container with contiguous
531  // storage
532  return {std::addressof(*begin(c)), std::forward<IndexVector>(indexes)};
533 }
534 
535 
536 static_assert(!Traits::has_subscript_operator<SubscriptOperation<int[100][100], const int *>, int>::value, "");
537 static_assert(!Traits::has_subscript_operator<SubscriptOperation<int[100][100], const int *>, int[4]>::value, "");
538 static_assert(!Traits::has_subscript_operator<SubscriptOperation<std::vector<int>, const int *>, int[4]>::value, "");
539 static_assert( Traits::has_subscript_operator<SubscriptOperation<int[100][100], const int[4]>, int>::value, "");
540 static_assert( Traits::has_subscript_operator<SubscriptOperation<int[100][100], const int[4]>, int[4]>::value, "");
541 static_assert(!Traits::has_subscript_operator<SubscriptOperation<std::vector<int>, const int[4]>, int[4]>::value, "");
542 
552 template <typename Container, typename I>
553 Vc_ALWAYS_INLINE Vc::Common::SubscriptOperation<
554  typename std::remove_reference<decltype(std::declval<Container>()[0])>::type,
555  const std::initializer_list<I> &> subscript_operator(Container &&vec,
556  const std::initializer_list<I> &indexes)
557 {
558  return {&vec[0], indexes};
559 }
560 //}}}1
561 
562 } // namespace Common
563 
564 using Common::subscript_operator;
565 
566 namespace Traits
567 {
568 template <typename T, typename IndexVector, typename Scale>
569 struct is_subscript_operation_internal<Common::SubscriptOperation<T, IndexVector, Scale>> : public std::true_type
570 {
571 };
572 } // namespace Traits
573 
574 using Common::subscript_operator;
575 
576 } // namespace Vc
577 
578 #endif // VC_COMMON_SUBSCRIPT_H_
579 
580 // vim: foldmethod=marker
Definition: vector.h:258
constexpr bool all_of(const Mask &m)
Returns whether all entries in the mask m are true.
Definition: algorithms.h:46