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