File: mdarray.hh

package info (click to toggle)
dune-common 2.10.0-6
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid
  • size: 5,824 kB
  • sloc: cpp: 52,256; python: 3,979; sh: 1,658; makefile: 17
file content (665 lines) | stat: -rw-r--r-- 26,543 bytes parent folder | download | duplicates (2)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
// -*- tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*-
// vi: set et ts=4 sw=2 sts=2:
// SPDX-FileCopyrightInfo: Copyright © DUNE Project contributors, see file LICENSE.md in module root
// SPDX-License-Identifier: LicenseRef-GPL-2.0-only-with-DUNE-exception
#ifndef DUNE_COMMON_STD_MDARRAY_HH
#define DUNE_COMMON_STD_MDARRAY_HH

#include <algorithm>
#include <array>
#include <memory>
#include <vector>
#include <tuple>
#include <type_traits>
#if __has_include(<version>)
  #include <version>
#endif

#include <dune/common/indices.hh>
#include <dune/common/rangeutilities.hh>
#include <dune/common/std/default_accessor.hh>
#include <dune/common/std/mdspan.hh>
#include <dune/common/std/memory.hh>
#include <dune/common/std/span.hh>
#include <dune/common/std/impl/containerconstructiontraits.hh>

namespace Dune::Std {

/**
 * \brief An owning multi-dimensional array analog of mdspan.
 * \ingroup CxxUtilities
 * \nosubgrouping
 *
 * The implementation is inspired by the mdarray c++ standard proposals
 * <a href="https://www.open-std.org/jtc1/sc22/wg21/docs/papers/2023/p1684r5.html">P1684R5</a>.
 *
 * \b Example:
 * \code{.cpp}
    // two-dimensional array with 2 rows and 6 columns of static shape
    Dune::Std::mdarray<double, Dune::Std::extents<std::size_t,2,6>> m1{};

    // same two-dimensional array but with dynamic shape
    Dune::Std::mdarray<double, Dune::Std::dextents<std::size_t,2>> m2{2,6};

    // storage type similar to the Dune::FieldMatrix
    template <class T, int m, int n>
    using FieldMatrix = Dune::Std::mdarray<T,
      Dune::Std::extents<int,m,n>, Dune::Std::layout_right, std::array<T,m*n>>;

    // store data into the array using two indices
    for (std::size_t i = 0; i != m1.extent(0); i++)
        for (std::size_t j = 0; j != m1.extent(1); j++)
            m1(i, j) = i + j;  // or m1[i, j]
 * \endcode
 *
 * \note The interface of `Std::mdarray` provides the `operator[]` with multiple
 *       indices overload only if available in c++23. For older c++ versions
 *       the `operator()` overload is implemented.
 *
 * \tparam Element  The element type; a complete object type that is neither an abstract
 *                  class type nor an array type.
 * \tparam Extents  Specifies number of dimensions, their sizes, and which are known at
 *                  compile time. Must be a specialization of `Std::extents`.
 * \tparam LayoutPolicy   Specifies how to convert multi-dimensional index to underlying flat index.
 * \tparam Container      A container type accessible by a single index provided by the layout mapping.
 **/
template <class Element, class Extents, class LayoutPolicy = Std::layout_right,
          class Container = std::vector<Element>>
class mdarray
{
  template <class,class,class,class> friend class mdarray;

  static_assert(std::is_object_v<Element>);
  static_assert(!std::is_abstract_v<Element>);
  static_assert(!std::is_array_v<Element>);
  static_assert(std::is_same_v<Element, typename Container::value_type>);

public:
  using element_type = Element;
  using extents_type = Extents;
  using layout_type = LayoutPolicy;
  using container_type = Container;

  using value_type = element_type;
  using mapping_type = typename layout_type::template mapping<extents_type>;

  using index_type = typename extents_type::index_type;
  using size_type = typename extents_type::size_type;
  using rank_type = typename extents_type::rank_type;

  using mdspan_type = mdspan<element_type,extents_type,layout_type>;
  using const_mdspan_type = mdspan<const element_type,extents_type,layout_type>;

  using pointer = decltype(Std::to_address(std::declval<container_type>().begin()));
  using reference = typename container_type::reference;
  using const_pointer = decltype(Std::to_address(std::declval<container_type>().cbegin()));
  using const_reference = typename container_type::const_reference;

  static_assert(std::is_constructible_v<mapping_type, extents_type>);

private:
  // helper function to construct the container
  template <class C, class... Args>
  static constexpr auto construct_container (Args&&... args)
    -> decltype(Impl::ContainerConstructionTraits<C>::construct(std::forward<Args>(args)...))
  {
    return Impl::ContainerConstructionTraits<C>::construct(std::forward<Args>(args)...);
  }

public:
  /// \name mdarray constructors
  /// @{

  /// \brief A default constructor; needed only if the constructor for dynamic extents does not apply
  template <class E = extents_type, class C = container_type, class M = mapping_type,
    std::enable_if_t<(E::rank_dynamic() != 0), int> = 0,
    std::enable_if_t<std::is_default_constructible_v<C>, int> = 0,
    std::enable_if_t<std::is_default_constructible_v<M>, int> = 0>
  constexpr mdarray ()
    : container_{}
    , mapping_{}
  {}


  // -------------------------------------
  // constructors from extents or mappings


  /// \brief Construct from the dynamic extents
  template <class... IndexTypes,
    std::enable_if_t<(... && std::is_convertible_v<IndexTypes,index_type>), int> = 0,
    std::enable_if_t<std::is_constructible_v<extents_type,IndexTypes...>, int> = 0,
    std::enable_if_t<(... && std::is_nothrow_constructible_v<index_type,IndexTypes>), int> = 0>
  explicit constexpr mdarray (IndexTypes... exts)
    : mdarray(extents_type(index_type(std::move(exts))...))
  {}

  /// \brief Construct from the extents of the array
  explicit constexpr mdarray (const extents_type& e)
    : mdarray(mapping_type(e))
  {}

  /// \brief Construct from the layout mapping of the array
  template <class C = container_type,
    decltype(construct_container<C>(std::declval<std::size_t>()), bool{}) = true>
  explicit constexpr mdarray (const mapping_type& m)
    : container_(construct_container<C>(m.required_span_size()))
    , mapping_(m)
  {}


  // ---------------------------------------
  // constructors with a given initial value

  /// \brief Construct from extents and initial value
  constexpr mdarray (const extents_type& e, const value_type& v)
    : mdarray(mapping_type(e), v)
  {}

  /// \brief Construct from layout mapping and initial value
  template <class C = container_type,
    decltype(construct_container<C>(std::declval<std::size_t>(),std::declval<const value_type&>()), bool{}) = true>
  constexpr mdarray (const mapping_type& m, const value_type& v)
    : container_(construct_container<C>(m.required_span_size(), v))
    , mapping_(m)
  {}


  // -----------------------------------
  // constructors with a given container

  /// \brief Construct from extents and the storage container
  template <class E = extents_type,
    std::enable_if_t<std::is_constructible_v<mapping_type,const E&>, int> = 0>
  constexpr mdarray (const E& e, const container_type& c)
    : container_(c)
    , mapping_(e)
  {}

  /// \brief Construct from extents and the storage container
  template <class E = extents_type,
    std::enable_if_t<std::is_constructible_v<mapping_type,const E&>, int> = 0>
  constexpr mdarray (const E& e, container_type&& c)
    : container_(std::move(c))
    , mapping_(e)
  {}

  /// \brief Construct from layout mapping and the storage container
  constexpr mdarray (const mapping_type& m, const container_type& c)
    : container_(c)
    , mapping_(m)
  {}

  /// \brief Construct from layout mapping and the storage container
  constexpr mdarray (const mapping_type& m, container_type&& c)
    : container_(std::move(c))
    , mapping_(m)
  {}


  // -----------------------
  // converting constructors

  /// \brief Converting constructor from other mdarray
  template <class OtherElementType, class OtherExtents, class OtherLayoutPolicy, class OtherContainer,
    std::enable_if_t<std::is_constructible_v<Container,const OtherContainer&>, int> = 0,
    std::enable_if_t<std::is_constructible_v<extents_type,OtherExtents>, int> = 0,
    std::enable_if_t<std::is_constructible_v<mapping_type,const typename OtherLayoutPolicy::template mapping<OtherExtents>&>, int> = 0>
  #if __cpp_conditional_explicit >= 201806L
  explicit(
    !std::is_convertible_v<const typename OtherLayoutPolicy::template mapping<OtherExtents>&, mapping_type> ||
    !std::is_convertible_v<const OtherContainer&, container_type>)
  #endif
  constexpr mdarray (const mdarray<OtherElementType,OtherExtents,OtherLayoutPolicy,OtherContainer>& other)
    : container_(other.container_)
    , mapping_(other.mapping_)
  {}

  /// \brief Converting constructor from mdspan
  template <class OtherElementType, class OtherExtents, class OtherLayoutPolicy, class Accessor,
    std::enable_if_t<std::is_constructible_v<value_type,typename Accessor::reference>, int> = 0,
    std::enable_if_t<std::is_assignable_v<typename Accessor::reference, value_type>, int> = 0,
    std::enable_if_t<std::is_constructible_v<mapping_type, const typename OtherLayoutPolicy::template mapping<OtherExtents>&>, int> = 0,
    decltype(construct_container<container_type>(std::declval<std::size_t>()), bool{}) = true>
  #if __cpp_conditional_explicit >= 201806L
  explicit(
    !std::is_convertible_v<const typename OtherLayoutPolicy::template mapping<OtherExtents>&, mapping_type> ||
    !std::is_convertible_v<typename Accessor::reference, value_type>)
  #endif
  constexpr mdarray (const mdspan<OtherElementType,OtherExtents,OtherLayoutPolicy,Accessor>& other)
    : container_(construct_container<container_type>(other.size()))
    , mapping_(other.mapping())
  {
    init_from_mdspan(other);
  }


  // ----------------------------
  // constructors with allocators

  /// \brief Construct from the extents of the array and allocator
  template <class Alloc,
    std::enable_if_t<std::is_constructible_v<container_type, std::size_t, Alloc>, int> = 0>
  constexpr mdarray (const extents_type& e, const Alloc& a)
    : mdarray(mapping_type(e), a)
  {}

  /// \brief Construct from the layout mapping of the array and allocator
  template <class Alloc,
    std::enable_if_t<std::is_constructible_v<container_type, std::size_t, Alloc>, int> = 0>
  constexpr mdarray (const mapping_type& m, const Alloc& a)
    : container_(m.required_span_size(), a)
    , mapping_(m)
  {}

  /// \brief Construct from extents, initial value and allocator
  template <class Alloc>
  constexpr mdarray (const extents_type& e, const value_type& v, const Alloc& a)
    : mdarray(mapping_type(e), v, a)
  {}

  /// \brief Construct from layout mapping, initial value and allocator
  template <class Alloc,
    std::enable_if_t<std::is_constructible_v<container_type, std::size_t, value_type, Alloc>, int> = 0>
  constexpr mdarray (const mapping_type& m, const value_type& v, const Alloc& a)
    : container_(m.required_span_size(), v, a)
    , mapping_(m)
  {}

  /// \brief Construct from extents, container and allocator
  template <class Alloc,
    std::enable_if_t<std::is_constructible_v<container_type, container_type, Alloc>, int> = 0>
  constexpr mdarray (const extents_type& e, const container_type& c, const Alloc& a)
    : container_(c, a)
    , mapping_(e)
  {}

  /// \brief Construct from extents, container and allocator
  template <class Alloc,
    std::enable_if_t<std::is_constructible_v<container_type, container_type, Alloc>, int> = 0>
  constexpr mdarray (const extents_type& e, container_type&& c, const Alloc& a)
    : container_(std::move(c), a)
    , mapping_(e)
  {}

  /// \brief Construct from layout mapping, container and allocator
  template <class Alloc,
    std::enable_if_t<std::is_constructible_v<container_type, container_type, Alloc>, int> = 0>
  constexpr mdarray (const mapping_type& m, const container_type& c, const Alloc& a)
    : container_(c, a)
    , mapping_(m)
  {}

  /// \brief Construct from layout mapping, container and allocator
  template <class Alloc,
    std::enable_if_t<std::is_constructible_v<container_type, container_type, Alloc>, int> = 0>
  constexpr mdarray (const mapping_type& m, container_type&& c, const Alloc& a)
    : container_(std::move(c), a)
    , mapping_(m)
  {}

  /// \brief Converting constructor with alternative allocator
  template <class V, class E, class L, class C, class Alloc,
    std::enable_if_t<std::is_constructible_v<container_type, C, Alloc>, int> = 0>
  #if __cpp_conditional_explicit >= 201806L
  explicit(
    !std::is_convertible_v<const typename L::template mapping<E>&, mapping_type> ||
    !std::is_convertible_v<const C&, container_type>)
  #endif
  constexpr mdarray (const mdarray<V,E,L,C>& other, const Alloc& a) noexcept
    : container_(other.container_, a)
    , mapping_(other.mapping_)
  {}

  /// \brief Converting constructor from mdspan
  template <class V, class E, class L, class A, class Alloc,
    class C = container_type,
    class Al = typename C::allocator_type,
    std::enable_if_t<std::is_constructible_v<C, std::size_t, Alloc>, int> = 0>
  #if __cpp_conditional_explicit >= 201806L
  explicit(
    !std::is_convertible_v<const typename L::template mapping<E>&, mapping_type> ||
    !std::is_convertible_v<typename A::reference, value_type> ||
    !std::is_convertible_v<Alloc, Al>)
  #endif
  constexpr mdarray (const mdspan<V,E,L,A>& other, const Alloc& a)
    : container_(other.size(), a)
    , mapping_(other.mapping_)
  {
    init_from_mdspan(other);
  }

  /// @}

private:

  template <class V, class E, class L, class A, class... Indices>
  void init_from_mdspan (const mdspan<V,E,L,A>& other, Indices... ii)
  {
    constexpr rank_type pos = sizeof...(Indices);
    if constexpr(pos < rank()) {
      for (typename E::index_type i = 0; i < other.extent(pos); ++i)
        init_from_mdspan(other,ii...,i);
    } else {
      using I = std::array<typename E::index_type,E::rank()>;
      container_[mapping_(index_type(ii)...)] = other[I{ii...}];
    }
  }

public:

  /// \name Multi index access
  /// @{

  /**
   * \brief Access element at position (i0,i1,...)
   * \note The `operator()` is not in the std proposal, but is provided for using mdspan without c++23.
   * For compatibility reasons it should only be used if the macro DUNE_HAVE_CXX_STD_MDARRAY is explicitly
   * set to 0.
   **/
  template <class... Indices,
    std::enable_if_t<(sizeof...(Indices) == extents_type::rank()), int> = 0,
    std::enable_if_t<(... && std::is_convertible_v<Indices,index_type>), int> = 0>
  constexpr reference operator() (Indices... indices)
  {
    return container_[mapping_(index_type(std::move(indices))...)];
  }

  /**
   * \brief Access element at position (i0,i1,...)
   * \note The `operator()` is not in the std proposal, but is provided for using mdspan without c++23.
   **/
  template <class... Indices,
    std::enable_if_t<(sizeof...(Indices) == extents_type::rank()), int> = 0,
    std::enable_if_t<(... && std::is_convertible_v<Indices,index_type>), int> = 0>
  constexpr const_reference operator() (Indices... indices) const
  {
    return container_[mapping_(index_type(std::move(indices))...)];
  }


#if __cpp_multidimensional_subscript >= 202110L

  /// \brief Access element at position [i0,i1,...]
  template <class... Indices,
    std::enable_if_t<(sizeof...(Indices) == extents_type::rank()), int> = 0,
    std::enable_if_t<(... && std::is_convertible_v<Indices,index_type>), int> = 0>
  constexpr reference operator[] (Indices... indices)
  {
    return container_[mapping_(index_type(std::move(indices))...)];
  }

  /// \brief Access element at position [i0,i1,...]
  template <class... Indices,
    std::enable_if_t<(sizeof...(Indices) == extents_type::rank()), int> = 0,
    std::enable_if_t<(... && std::is_convertible_v<Indices,index_type>), int> = 0>
  constexpr const_reference operator[] (Indices... indices) const
  {
    return container_[mapping_(index_type(std::move(indices))...)];
  }

#else

  /// \brief Access specified element at position [i0]
  /// For a rank one mdarray, the operator[i] is added to support bracket access before __cpp_multidimensional_subscript is supported.
  template <class Index, class E = extents_type,
    std::enable_if_t<std::is_convertible_v<Index,index_type>, int> = 0,
    std::enable_if_t<(E::rank() == 1), int> = 0>
  constexpr reference operator[] (Index index)
  {
    return container_[mapping_(index_type(std::move(index)))];
  }

  /// \brief Access specified element at position [i0]
  /// For a rank one mdarray, the operator[i] is added to support bracket access before __cpp_multidimensional_subscript is supported.
  template <class Index, class E = extents_type,
    std::enable_if_t<std::is_convertible_v<Index,index_type>, int> = 0,
    std::enable_if_t<(E::rank() == 1), int> = 0>
  constexpr const_reference operator[] (Index index) const
  {
    return container_[mapping_(index_type(std::move(index)))];
  }

#endif


  /// \brief Access element at position [{i0,i1,...}]
  template <class Index,
    std::enable_if_t<std::is_convertible_v<const Index&, index_type>, int> = 0>
  constexpr reference operator[] (Std::span<Index,extents_type::rank()> indices)
  {
    return unpackIntegerSequence([&](auto... ii) -> reference {
      return container_[mapping_(index_type(indices[ii])...)]; },
      std::make_index_sequence<extents_type::rank()>{});
  }

  /// \brief Access element at position [{i0,i1,...}]
  template <class Index,
    std::enable_if_t<std::is_convertible_v<const Index&, index_type>, int> = 0>
  constexpr const_reference operator[] (Std::span<Index,extents_type::rank()> indices) const
  {
    return unpackIntegerSequence([&](auto... ii) -> const_reference {
      return container_[mapping_(index_type(indices[ii])...)]; },
      std::make_index_sequence<extents_type::rank()>{});
  }

  /// \brief Access element at position [{i0,i1,...}]
  template <class Index,
    std::enable_if_t<std::is_convertible_v<const Index&, index_type>, int> = 0>
  constexpr reference operator[] (const std::array<Index,extents_type::rank()>& indices)
  {
    return std::apply([&](auto... ii) -> reference {
      return container_[mapping_(index_type(ii)...)]; }, indices);
  }

  /// \brief Access element at position [{i0,i1,...}]
  template <class Index,
    std::enable_if_t<std::is_convertible_v<const Index&, index_type>, int> = 0>
  constexpr const_reference operator[] (const std::array<Index,extents_type::rank()>& indices) const
  {
    return std::apply([&](auto... ii) -> const_reference {
      return container_[mapping_(index_type(ii)...)]; }, indices);
  }

  /// @}


  /// \brief Number of elements in all dimensions of the array, \related extents
  constexpr const extents_type& extents () const noexcept { return mapping_.extents(); }

  /// \brief Index mapping of a layout policy
  constexpr const mapping_type& mapping () const noexcept { return mapping_; }

  /// \brief The underlying storage container
  constexpr const container_type& container () const noexcept { return container_; }

  /**
   * \brief Move the container out of the mdarray.
   *
   * Note that after moving out the container the mdarray remains in a valid but an unspecified state.
   * One can destruct it or move-assign to it (i.e. restore the container in some way).
   **/
  constexpr container_type&& extract_container () && noexcept { return std::move(container_); }


  /// \name Size information
  /// @{

  /// \brief Number of dimensions of the array
  static constexpr rank_type rank () noexcept { return extents_type::rank(); }

  /// \brief Number of dimension with dynamic size
  static constexpr rank_type rank_dynamic () noexcept { return extents_type::rank_dynamic(); }

  /// \brief Number of elements in the r'th dimension of the tensor
  static constexpr std::size_t static_extent (rank_type r) noexcept { return extents_type::static_extent(r); }

  /// \brief Number of elements in the r'th dimension of the tensor
  constexpr index_type extent (rank_type r) const noexcept { return extents().extent(r); }

  /// \brief Size of the multi-dimensional index space
  constexpr size_type size () const noexcept
  {
    size_type s = 1;
    for (rank_type r = 0; r < rank(); ++r)
      s *= extent(r);
    return s;
  }

  /// \brief Size of the underlying container
  constexpr std::size_t container_size () const { return container_.size(); }

  /// \brief Check whether the index space is empty
  [[nodiscard]] constexpr bool empty () const noexcept { return size() == 0; }

  /// \brief The stride along the specified dimension
  constexpr index_type stride (rank_type r) const { return mapping().stride(r); };

  /// @}


  static constexpr bool is_always_unique () noexcept { return mapping_type::is_always_unique(); }
  static constexpr bool is_always_exhaustive () noexcept { return mapping_type::is_always_exhaustive(); }
  static constexpr bool is_always_strided () noexcept { return mapping_type::is_always_strided(); }

  constexpr bool is_unique () const noexcept { return mapping_.is_unique(); }
  constexpr bool is_exhaustive () const noexcept { return mapping_.is_exhaustive(); }
  constexpr bool is_strided () const noexcept { return mapping_.is_strided(); }


  /// \name Direct access to the data
  /// @{

  /// \brief Direct access to the underlying data in the container
  constexpr pointer container_data () noexcept { return Std::to_address(container_.begin()); }

  /// \brief Direct access to the underlying const data in the container
  constexpr const_pointer container_data () const noexcept { return Std::to_address(container_.begin()); }

  /// @}


  friend constexpr void swap (mdarray& x, mdarray& y) noexcept
  {
    using std::swap;
    swap(x.container_, y.container_);
    swap(x.mapping_, y.mapping_);
  }


  /// \name Comparison operators
  /// @{

  friend constexpr bool operator== (const mdarray& lhs, const mdarray& rhs) noexcept
  {
    return lhs.mapping() == rhs.mapping() && lhs.container() == rhs.container();
  }

  /// @}

  /// \name Conversion into mdspan
  /// @{

  /// \brief Conversion operator to mdspan
  template <class V, class E, class L, class A,
    std::enable_if_t<std::is_assignable_v<mdspan<V,E,L,A>, mdspan_type>, int> = 0>
  constexpr operator mdspan<V,E,L,A> ()
  {
    return mdspan_type(container_data(), mapping());
  }

  /// \brief Conversion operator to mdspan
  template <class V, class E, class L, class A,
    std::enable_if_t<std::is_assignable_v<mdspan<V,E,L,A>, const_mdspan_type>, int> = 0>
  constexpr operator mdspan<V,E,L,A> () const
  {
    return const_mdspan_type(container_data(), mapping());
  }

  /// \brief Conversion function to mdspan
  template <class AccessorPolicy = Std::default_accessor<element_type>,
    std::enable_if_t<
      std::is_assignable_v<mdspan_type, mdspan<element_type,extents_type,layout_type,AccessorPolicy>>, int> = 0>
  constexpr mdspan<element_type,extents_type,layout_type,AccessorPolicy>
  to_mdspan (const AccessorPolicy& a = AccessorPolicy{})
  {
    return mdspan<element_type,extents_type,layout_type,AccessorPolicy>(container_data(), mapping(), a);
  }

  /// \brief Conversion function to mdspan
  template <class AccessorPolicy = Std::default_accessor<const element_type>,
    std::enable_if_t<
      std::is_assignable_v<const_mdspan_type, mdspan<const element_type,extents_type,layout_type,AccessorPolicy>>, int> = 0>
  constexpr mdspan<const element_type,extents_type,layout_type,AccessorPolicy>
  to_mdspan (const AccessorPolicy& a = AccessorPolicy{}) const
  {
    return mdspan<const element_type,extents_type,layout_type,AccessorPolicy>(container_data(), mapping(), a);
  }

protected:
  container_type container_;
  [[no_unique_address]] mapping_type mapping_;
};

/// \name Deduction guides
/// \relates mdarray
/// @{

template <class IndexType, std::size_t... exts, class Container>
mdarray (const Std::extents<IndexType, exts...>&, const Container&)
  -> mdarray<typename Container::value_type, Std::extents<IndexType, exts...>, layout_right, Container>;

template <class Mapping, class Container>
mdarray (const Mapping&, const Container&)
  -> mdarray<typename Container::value_type, typename Mapping::extents_type, typename Mapping::layout_type, Container>;

template <class IndexType, std::size_t... exts, class Container>
mdarray (const Std::extents<IndexType, exts...>&, Container&&)
  -> mdarray<typename Container::value_type, Std::extents<IndexType, exts...>, layout_right, Container>;

template <class Mapping, class Container>
mdarray (const Mapping&, Container&&)
  -> mdarray<typename Container::value_type, typename Mapping::extents_type, typename Mapping::layout_type, Container>;

template <class Element, class Extents, class Layout, class Accessor>
mdarray (const mdspan<Element, Extents, Layout, Accessor>&)
  -> mdarray<std::remove_cv_t<Element>, Extents, Layout>;

template <class IndexType, std::size_t... exts, class Container, class Alloc>
mdarray (const Std::extents<IndexType, exts...>&, const Container&, const Alloc&)
  -> mdarray<typename Container::value_type, Std::extents<IndexType, exts...>, layout_right, Container>;

template <class Mapping, class Container, class Alloc>
mdarray (const Mapping&, const Container&, const Alloc&)
  -> mdarray<typename Container::value_type, typename Mapping::extents_type, typename Mapping::layout_type, Container>;

template <class IndexType, std::size_t... exts, class Container, class Alloc>
mdarray (const Std::extents<IndexType, exts...>&, Container&&, const Alloc&)
  -> mdarray<typename Container::value_type, Std::extents<IndexType, exts...>, layout_right, Container>;

template <class Mapping, class Container, class Alloc>
mdarray (const Mapping&, Container&&, const Alloc&)
  -> mdarray<typename Container::value_type, typename Mapping::extents_type, typename Mapping::layout_type, Container>;

template <class Element, class Extents, class Layout, class Accessor, class Alloc>
mdarray (const mdspan<Element, Extents, Layout, Accessor>&, const Alloc&)
  -> mdarray<std::remove_cv_t<Element>, Extents, Layout>;

/// @}

/// \name Additional deduction guide for mdspan
/// \relates mdspan
/// @{

template <class Element, class Extents, class Layout, class Container>
mdspan (mdarray<Element, Extents, Layout, Container>) -> mdspan<
  typename decltype(std::declval<mdarray<Element, Extents, Layout, Container>>().to_mdspan())::element_type,
  typename decltype(std::declval<mdarray<Element, Extents, Layout, Container>>().to_mdspan())::extents_type,
  typename decltype(std::declval<mdarray<Element, Extents, Layout, Container>>().to_mdspan())::layout_type,
  typename decltype(std::declval<mdarray<Element, Extents, Layout, Container>>().to_mdspan())::accessor_type>;

/// @}

} // end namespace Dune::Std

#endif // DUNE_COMMON_STD_MDARRAY_HH