Merge remote-tracking branch 'aosp/upstream-master' into HEAD am: a78a14d02f
am: c1b94b10a1

Change-Id: I8541109c3fb10caea0cf16eeb770753c1fc33839
diff --git a/.gitignore b/.gitignore
index d452689..cbb4a5c 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,4 +1,6 @@
 CMakeFiles
+build
+include/catch
 tests/CMakeFiles
 tests/Debug
 *.opensdf
@@ -11,3 +13,5 @@
 Testing/Temporary/*.*
 CMakeCache.txt
 *.suo
+.vs/
+.vscode/
diff --git a/.gitmodules b/.gitmodules
deleted file mode 100644
index d9229ae..0000000
--- a/.gitmodules
+++ /dev/null
@@ -1,3 +0,0 @@
-[submodule "tests/unittest-cpp"]
-	path = tests/unittest-cpp
-	url = https://github.com/Microsoft/unittest-cpp.git
diff --git a/.travis.yml b/.travis.yml
index 5c69187..82b8ec2 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -10,7 +10,7 @@
     - ${TRAVIS_BUILD_DIR}/deps/llvm-3.6.2/install
     - ${TRAVIS_BUILD_DIR}/deps/llvm-3.7.1/install
     - ${TRAVIS_BUILD_DIR}/deps/llvm-3.8.1/install
-    - ${TRAVIS_BUILD_DIR}/deps/llvm-3.9.0/install
+    - ${TRAVIS_BUILD_DIR}/deps/llvm-3.9.1/install
 
 matrix:
   include:
@@ -61,6 +61,19 @@
     - env: CLANG_VERSION=3.8 BUILD_TYPE=Release
       os: linux
       addons: *clang38
+    - env: CLANG_VERSION=3.9 BUILD_TYPE=Debug
+      os: linux
+      addons: &clang39
+        apt:
+          packages:
+            - clang-3.9
+            - g++-5
+          sources: &sources
+            - ubuntu-toolchain-r-test
+            - llvm-toolchain-precise-3.9
+    - env: CLANG_VERSION=3.9 BUILD_TYPE=Release
+      os: linux
+      addons: *clang39
     - env: GCC_VERSION=5 BUILD_TYPE=Debug
       os: linux
       addons: &gcc5
@@ -93,7 +106,7 @@
   - |
     if [[ "${TRAVIS_OS_NAME}" == "linux" ]]; then
       if [[ -z "$(ls -A ${DEPS_DIR}/cmake/bin)" ]]; then
-        CMAKE_URL="https://cmake.org/files/v3.6/cmake-3.6.2-Linux-x86_64.tar.gz"
+        CMAKE_URL="https://cmake.org/files/v3.7/cmake-3.7.2-Linux-x86_64.tar.gz"
         mkdir -p cmake && travis_retry wget --no-check-certificate --quiet -O - "${CMAKE_URL}" | tar --strip-components=1 -xz -C cmake
       fi
       export PATH="${DEPS_DIR}/cmake/bin:${PATH}"
@@ -110,7 +123,7 @@
       if [[ "$CLANG_VERSION" == "3.6" ]]; then LLVM_VERSION="3.6.2"; fi
       if [[ "$CLANG_VERSION" == "3.7" ]]; then LLVM_VERSION="3.7.1"; fi
       if [[ "$CLANG_VERSION" == "3.8" ]]; then LLVM_VERSION="3.8.1"; fi
-      if [[ "$CLANG_VERSION" == "3.9" ]]; then LLVM_VERSION="3.9.0"; fi
+      if [[ "$CLANG_VERSION" == "3.9" ]]; then LLVM_VERSION="3.9.1"; fi
       LLVM_ROOT="${DEPS_DIR}/llvm-${LLVM_VERSION}"
       LLVM_URL="http://llvm.org/releases/${LLVM_VERSION}/llvm-${LLVM_VERSION}.src.tar.xz"
       LIBCXX_URL="http://llvm.org/releases/${LLVM_VERSION}/libcxx-${LLVM_VERSION}.src.tar.xz"
diff --git a/CMakeLists.txt b/CMakeLists.txt
index 2f1b0f1..e62a1d6 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -1,25 +1,57 @@
-cmake_minimum_required(VERSION 2.8.7)
+cmake_minimum_required(VERSION 3.1.3)
 
 project(GSL CXX)
 
-set(GSL_HEADERS
-    "gsl/gsl"
-    "gsl/gsl_assert"
-    "gsl/gsl_byte"
-    "gsl/gsl_util"
-    "gsl/multi_span"
-    "gsl/span"
-    "gsl/string_span"
+include(ExternalProject)
+find_package(Git REQUIRED)
+
+# creates a library GSL which is an interface (header files only)
+add_library(GSL INTERFACE)
+
+# when minimum version required is 3.8.0 remove if below
+# both branches do exactly the same thing
+if (CMAKE_MAJOR_VERSION VERSION_LESS 3.7.9)
+    if (NOT MSVC)
+        include(CheckCXXCompilerFlag)
+        CHECK_CXX_COMPILER_FLAG("-std=c++14" COMPILER_SUPPORTS_CXX14)
+        if(COMPILER_SUPPORTS_CXX14)
+            target_compile_options(GSL INTERFACE "-std=c++14")
+        else()
+            message(FATAL_ERROR "The compiler ${CMAKE_CXX_COMPILER} has no C++14 support. Please use a different C++ compiler.")
+        endif()
+
+    endif()
+else ()
+    # set the GSL library to be compiled only with c++14
+    target_compile_features(GSL INTERFACE cxx_std_14)
+    # on *nix systems force the use of -std=c++XX instead of -std=gnu++XX (default)
+    set(CMAKE_CXX_EXTENSIONS OFF)
+endif()
+
+# add definitions to the library and targets that consume it
+target_compile_definitions(GSL INTERFACE
+    $<$<CXX_COMPILER_ID:MSVC>:
+        # remove unnecessary warnings about unchecked iterators
+        _SCL_SECURE_NO_WARNINGS
+    >
 )
 
-include_directories(
-    ${CMAKE_CURRENT_BINARY_DIR}
+# add include folders to the library and targets that consume it
+target_include_directories(GSL INTERFACE
+    $<BUILD_INTERFACE:
+        ${CMAKE_CURRENT_SOURCE_DIR}/include
+    >
 )
 
-install(FILES ${GSL_HEADERS}
-    DESTINATION include/gsl
+# add natvis file to the library so it will automatically be loaded into Visual Studio
+target_sources(GSL INTERFACE
+    ${CMAKE_CURRENT_SOURCE_DIR}/GSL.natvis
+)
+
+install(
+    DIRECTORY include/gsl
+    DESTINATION include
 )
 
 enable_testing()
-
 add_subdirectory(tests)
diff --git a/GSL.natvis b/GSL.natvis
new file mode 100644
index 0000000..e40a8c9
--- /dev/null
+++ b/GSL.natvis
@@ -0,0 +1,98 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- 
+    This will make GitHub and some editors recognize this code as XML: 
+    vim: syntax=xml
+-->
+<AutoVisualizer xmlns="http://schemas.microsoft.com/vstudio/debugger/natvis/2010">
+    <!-- These types are from the gsl_assert header. -->
+    <Type Name="gsl::fail_fast">
+        <!-- na hides the address, otherwise it would appear as 0x.... "Message" -->
+        <DisplayString>{_Data._What,nasb}</DisplayString>
+    </Type>
+
+    <!-- These types are from the gsl_util header. -->
+    <Type Name="gsl::final_act&lt;*&gt;">
+        <DisplayString>{{ invoke = {invoke_}, action = {f_} }}</DisplayString>
+        <Expand>
+            <Item Name="[invoke]">invoke_</Item>
+            <Item Name="[callback]">f_</Item>
+        </Expand>
+    </Type>
+
+    <!-- These types are from the span header. -->
+    <!-- This is for dynamic_extent spans. -->
+    <Type Name="gsl::span&lt;*, -1&gt;">
+        <DisplayString>{{ extent = {storage_.size_} }}</DisplayString>
+        <Expand>
+            <ArrayItems>
+                <Size>storage_.size_</Size>
+                <ValuePointer>storage_.data_</ValuePointer>
+            </ArrayItems>
+        </Expand>
+    </Type>
+
+    <!-- This works for constexpr size spans. -->
+    <Type Name="gsl::span&lt;*, *&gt;">
+        <DisplayString>{{ extent = {extent} }}</DisplayString>
+        <Expand>
+            <ArrayItems>
+                <Size>extent</Size>
+                <ValuePointer>storage_.data_</ValuePointer>
+            </ArrayItems>
+        </Expand>
+    </Type>
+
+    <!-- This is for dynamic_extent string_spans. -->
+    <Type Name="gsl::basic_string_span&lt;*, -1&gt;">
+        <DisplayString>{span_.storage_.data_,[span_.storage_.size_]na}</DisplayString>
+        <Expand>
+            <Item Name="[size]">span_.storage_.size_</Item>
+            <ArrayItems>
+                <Size>span_.storage_.size_</Size>
+                <ValuePointer>span_.storage_.data_</ValuePointer>
+            </ArrayItems>
+        </Expand>
+    </Type>
+
+    <!-- This works for constexpr size string_spans. -->
+    <Type Name="gsl::basic_string_span&lt;*, *&gt;">
+        <DisplayString>{span_.storage_.data_,[span_.extent]na}</DisplayString>
+        <Expand>
+            <Item Name="[size]">span_.extent</Item>
+            <ArrayItems>
+                <Size>span_.extent</Size>
+                <ValuePointer>span_.storage_.data_</ValuePointer>
+            </ArrayItems>
+        </Expand>
+    </Type>
+
+    <!-- This is for dynamic_extent zstring_spans. -->
+    <Type Name="gsl::basic_zstring_span&lt;*, -1&gt;">
+        <DisplayString>{span_.storage_.data_,[span_.storage_.size_]na}</DisplayString>
+        <Expand>
+            <Item Name="[size]">span_.storage_.size_</Item>
+            <ArrayItems>
+                <Size>span_.storage_.size_</Size>
+                <ValuePointer>span_.storage_.data_</ValuePointer>
+            </ArrayItems>
+        </Expand>
+    </Type>
+    
+    <!-- This works for constexpr size string_spans. -->
+    <Type Name="gsl::basic_zstring_span&lt;*, *&gt;">
+        <DisplayString>{span_.storage_.data_,[span_.extent]na}</DisplayString>
+        <Expand>
+            <Item Name="[size]">span_.extent</Item>
+            <ArrayItems>
+                <Size>span_.extent</Size>
+                <ValuePointer>span_.storage_.data_</ValuePointer>
+            </ArrayItems>
+        </Expand>
+    </Type>
+    
+    <!-- These types are from the gsl header. -->
+    <Type Name="gsl::not_null&lt;*&gt;">
+        <!-- We can always dereference this since it's an invariant. -->
+        <DisplayString>value = {*ptr_}</DisplayString>
+    </Type>
+</AutoVisualizer>  
diff --git a/README.md b/README.md
index c3aff66..243b7cd 100644
--- a/README.md
+++ b/README.md
@@ -6,10 +6,10 @@
 
 The library includes types like `span<T>`, `string_span`, `owner<>` and others.
 
-The entire implementation is provided inline in the headers under the [gsl](./gsl) directory. The implementation generally assumes a platform that implements C++14 support. There are specific workarounds to support MSVC 2013 and 2015.
+The entire implementation is provided inline in the headers under the [gsl](./include/gsl) directory. The implementation generally assumes a platform that implements C++14 support. There are specific workarounds to support MSVC 2015.
 
-While some types have been broken out into their own headers (e.g. [gsl/span](./gsl/span)),
-it is simplest to just include [gsl/gsl](./gsl/gsl) and gain access to the entire library.
+While some types have been broken out into their own headers (e.g. [gsl/span](./include/gsl/span)),
+it is simplest to just include [gsl/gsl](./include/gsl/gsl) and gain access to the entire library.
 
 > NOTE: We encourage contributions that improve or refine any of the types in this library as well as ports to
 other platforms. Please see [CONTRIBUTING.md](./CONTRIBUTING.md) for more information about contributing.
@@ -17,18 +17,22 @@
 # Project Code of Conduct
 This project has adopted the [Microsoft Open Source Code of Conduct](https://opensource.microsoft.com/codeofconduct/). For more information see the [Code of Conduct FAQ](https://opensource.microsoft.com/codeofconduct/faq/) or contact [opencode@microsoft.com](mailto:opencode@microsoft.com) with any additional questions or comments.
 
+# Usage of Third Party Libraries
+This project makes use of the [Catch](https://github.com/philsquared/catch) testing library. Please see the [ThirdPartyNotices.txt](./ThirdPartyNotices.txt) file for details regarding the licensing of Catch.
+
 # Quick Start
 ## Supported Platforms
 The test suite that exercises GSL has been built and passes successfully on the following platforms:<sup>1)</sup>
 
-* Windows using Visual Studio 2013
 * Windows using Visual Studio 2015
+* Windows using Visual Studio 2017
 * Windows using Clang/LLVM 3.6
 * Windows using GCC 5.1
 * GNU/Linux using Clang/LLVM 3.6
 * GNU/Linux using GCC 5.1
-* OS X Yosemite using Xcode with AppleClang 7.0.0.7000072
+* OS X Yosemite using Xcode with Apple Clang 7.0.0.7000072
 * OS X Yosemite using GCC-5.2.0
+* OS X Sierra 10.12.4 using Apple LLVM version 8.1.0 (Clang-802.0.42)
 * FreeBSD 10.x with Clang/LLVM 3.6
 
 > If you successfully port GSL to another platform, we would love to hear from you. Please submit an issue to let us know. Also please consider
@@ -39,9 +43,7 @@
 ## Building the tests
 To build the tests, you will require the following:
 
-* [CMake](http://cmake.org), version 2.8.7 or later to be installed and in your PATH.
-* [UnitTest-cpp](https://github.com/Microsoft/unittest-cpp), to be cloned under the [tests/unittest-cpp](./tests/unittest-cpp) directory
-of your GSL source.
+* [CMake](http://cmake.org), version 3.1.3 or later to be installed and in your PATH.
 
 These steps assume the source code of this repository has been cloned into a directory named `c:\GSL`.
 
@@ -55,11 +57,11 @@
 
         cmake -G "Visual Studio 14 2015" c:\GSL
 
-3. Build the test suite (in this case, in the Debug configuration, Release is another good choice).    
+3. Build the test suite (in this case, in the Debug configuration, Release is another good choice).
 
         cmake --build . --config Debug
 
-4. Run the test suite.    
+4. Run the test suite.
 
         ctest -C Debug
 
@@ -68,19 +70,22 @@
 ## Using the libraries
 As the types are entirely implemented inline in headers, there are no linking requirements.
 
-You can copy the [gsl](./gsl) directory into your source tree so it is available
+You can copy the [gsl](./include/gsl) directory into your source tree so it is available
 to your compiler, then include the appropriate headers in your program.
 
-Alternatively set your compiler's *include path* flag to point to the GSL development folder (`c:\GSL` in the example above) or installation folder (after running the install). Eg.
+Alternatively set your compiler's *include path* flag to point to the GSL development folder (`c:\GSL\include` in the example above) or installation folder (after running the install). Eg.
 
 MSVC++
 
-    /I c:\GSL
+    /I c:\GSL\include
 
 GCC/clang
 
-    -I$HOME/dev/GSL
+    -I$HOME/dev/GSL/include
 
 Include the library using:
 
     #include <gsl/gsl>
+
+## Debugging visualization support
+For Visual Studio users, the file [GSL.natvis](./GSL.natvis) in the root directory of the repository can be added to your project if you would like more helpful visualization of GSL types in the Visual Studio debugger than would be offered by default.
diff --git a/ThirdPartyNotices.txt b/ThirdPartyNotices.txt
new file mode 100644
index 0000000..94b9acc
--- /dev/null
+++ b/ThirdPartyNotices.txt
@@ -0,0 +1,39 @@
+
+THIRD-PARTY SOFTWARE NOTICES AND INFORMATION
+Do Not Translate or Localize
+
+GSL: Guideline Support Library incorporates third party material from the projects listed below. The original copyright notice and the license under which Microsoft received such third party material are set forth below. Microsoft reserves all other rights not expressly granted, whether by implication, estoppel or otherwise.  
+
+
+1.	Catch (https://github.com/philsquared/Catch)
+
+
+%% Catch NOTICES, INFORMATION, AND LICENSE BEGIN HERE
+=========================================
+Boost Software License - Version 1.0 - August 17th, 2003
+
+Permission is hereby granted, free of charge, to any person or organization
+obtaining a copy of the software and accompanying documentation covered by
+this license (the "Software") to use, reproduce, display, distribute,
+execute, and transmit the Software, and to prepare derivative works of the
+Software, and to permit third-parties to whom the Software is furnished to
+do so, all subject to the following:
+
+The copyright notices in the Software and this entire statement, including
+the above license grant, this restriction and the following disclaimer,
+must be included in all copies of the Software, in whole or in part, and
+all derivative works of the Software, unless such copies or derivative
+works are solely in the form of machine-executable object code generated by
+a source language processor.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT
+SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE
+FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE,
+ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+DEALINGS IN THE SOFTWARE.
+=========================================
+END OF Catch NOTICES, INFORMATION, AND LICENSE
+
+
diff --git a/appveyor.yml b/appveyor.yml
new file mode 100644
index 0000000..d978a5d
--- /dev/null
+++ b/appveyor.yml
@@ -0,0 +1,47 @@
+shallow_clone: true
+
+platform:
+  - x86
+  - x64
+
+configuration:
+  - Debug
+  - Release
+
+image:
+  - Visual Studio 2015
+  - Visual Studio 2017
+
+cache:
+  - C:\cmake-3.8.0-win32-x86
+
+install:
+  - ps: |
+      if (![IO.File]::Exists("C:\cmake-3.8.0-win32-x86\bin\cmake.exe")) {
+        Start-FileDownload 'https://cmake.org/files/v3.8/cmake-3.8.0-win32-x86.zip'
+        7z x -y cmake-3.8.0-win32-x86.zip -oC:\
+      }
+      $env:PATH="C:\cmake-3.8.0-win32-x86\bin;$env:PATH"
+
+before_build:
+  - ps: |
+      mkdir build
+      cd build
+      if ("$env:APPVEYOR_JOB_NAME" -match "Image: Visual Studio 2015") {
+          $env:generator="Visual Studio 14 2015"
+      } else {
+          $env:generator="Visual Studio 15 2017"
+      }
+      if ($env:PLATFORM -eq "x64") {
+          $env:generator="$env:generator Win64"
+      }
+      echo generator="$env:generator"
+      cmake .. -G "$env:generator"
+
+build_script:
+  - cmake --build . --config %CONFIGURATION% -- /m /v:minimal
+
+test_script:
+  - ctest -j2
+
+deploy: off
diff --git a/gsl/gsl b/gsl/gsl
deleted file mode 100644
index 12a9676..0000000
--- a/gsl/gsl
+++ /dev/null
@@ -1,172 +0,0 @@
-///////////////////////////////////////////////////////////////////////////////
-//
-// Copyright (c) 2015 Microsoft Corporation. All rights reserved.
-//
-// This code is licensed under the MIT License (MIT).
-//
-// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
-// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
-// THE SOFTWARE.
-//
-///////////////////////////////////////////////////////////////////////////////
-
-#pragma once
-
-#ifndef GSL_GSL_H
-#define GSL_GSL_H
-
-#include "gsl_assert"  // Ensures/Expects
-#include "gsl_util"    // finally()/narrow()/narrow_cast()...
-#include "multi_span"  // multi_span, strided_span...
-#include "span"        // span
-#include "string_span" // zstring, string_span, zstring_builder...
-#include <memory>
-
-#ifdef _MSC_VER
-
-// No MSVC does constexpr fully yet
-#pragma push_macro("constexpr")
-#define constexpr /*constexpr*/
-
-// MSVC 2013 workarounds
-#if _MSC_VER <= 1800
-// noexcept is not understood
-#pragma push_macro("noexcept")
-#define noexcept /*noexcept*/
-
-// turn off some misguided warnings
-#pragma warning(push)
-#pragma warning(disable : 4351) // warns about newly introduced aggregate initializer behavior
-
-#endif // _MSC_VER <= 1800
-
-#endif // _MSC_VER
-
-namespace gsl
-{
-
-//
-// GSL.owner: ownership pointers
-//
-using std::unique_ptr;
-using std::shared_ptr;
-
-template <class T>
-using owner = T;
-
-//
-// not_null
-//
-// Restricts a pointer or smart pointer to only hold non-null values.
-//
-// Has zero size overhead over T.
-//
-// If T is a pointer (i.e. T == U*) then
-// - allow construction from U* or U&
-// - disallow construction from nullptr_t
-// - disallow default construction
-// - ensure construction from U* fails with nullptr
-// - allow implicit conversion to U*
-//
-template <class T>
-class not_null
-{
-    static_assert(std::is_assignable<T&, std::nullptr_t>::value, "T cannot be assigned nullptr.");
-
-public:
-    not_null(T t) : ptr_(t) { ensure_invariant(); }
-    not_null& operator=(const T& t)
-    {
-        ptr_ = t;
-        ensure_invariant();
-        return *this;
-    }
-
-    not_null(const not_null& other) = default;
-    not_null& operator=(const not_null& other) = default;
-
-    template <typename U, typename Dummy = std::enable_if_t<std::is_convertible<U, T>::value>>
-    not_null(const not_null<U>& other)
-    {
-        *this = other;
-    }
-
-    template <typename U, typename Dummy = std::enable_if_t<std::is_convertible<U, T>::value>>
-    not_null& operator=(const not_null<U>& other)
-    {
-        ptr_ = other.get();
-        return *this;
-    }
-
-    // prevents compilation when someone attempts to assign a nullptr
-    not_null(std::nullptr_t) = delete;
-    not_null(int) = delete;
-    not_null<T>& operator=(std::nullptr_t) = delete;
-    not_null<T>& operator=(int) = delete;
-
-    T get() const
-    {
-#ifdef _MSC_VER
-        __assume(ptr_ != nullptr);
-#endif
-        return ptr_;
-    } // the assume() should help the optimizer
-
-    operator T() const { return get(); }
-    T operator->() const { return get(); }
-
-    bool operator==(const T& rhs) const { return ptr_ == rhs; }
-    bool operator!=(const T& rhs) const { return !(*this == rhs); }
-private:
-    T ptr_;
-
-    // we assume that the compiler can hoist/prove away most of the checks inlined from this
-    // function
-    // if not, we could make them optional via conditional compilation
-    void ensure_invariant() const { Expects(ptr_ != nullptr); }
-
-    // unwanted operators...pointers only point to single objects!
-    // TODO ensure all arithmetic ops on this type are unavailable
-    not_null<T>& operator++() = delete;
-    not_null<T>& operator--() = delete;
-    not_null<T> operator++(int) = delete;
-    not_null<T> operator--(int) = delete;
-    not_null<T>& operator+(size_t) = delete;
-    not_null<T>& operator+=(size_t) = delete;
-    not_null<T>& operator-(size_t) = delete;
-    not_null<T>& operator-=(size_t) = delete;
-};
-
-} // namespace gsl
-
-namespace std
-{
-template <class T>
-struct hash<gsl::not_null<T>>
-{
-    size_t operator()(const gsl::not_null<T>& value) const { return hash<T>{}(value); }
-};
-
-} // namespace std
-
-#ifdef _MSC_VER
-
-#undef constexpr
-#pragma pop_macro("constexpr")
-
-#if _MSC_VER <= 1800
-
-#undef noexcept
-#pragma pop_macro("noexcept")
-
-#pragma warning(pop)
-
-#endif // _MSC_VER <= 1800
-
-#endif // _MSC_VER
-
-#endif // GSL_GSL_H
diff --git a/gsl/gsl_assert b/gsl/gsl_assert
deleted file mode 100644
index 6d8760d..0000000
--- a/gsl/gsl_assert
+++ /dev/null
@@ -1,85 +0,0 @@
-///////////////////////////////////////////////////////////////////////////////
-//
-// Copyright (c) 2015 Microsoft Corporation. All rights reserved.
-//
-// This code is licensed under the MIT License (MIT).
-//
-// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
-// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
-// THE SOFTWARE.
-//
-///////////////////////////////////////////////////////////////////////////////
-
-#pragma once
-
-#ifndef GSL_CONTRACTS_H
-#define GSL_CONTRACTS_H
-
-#include <exception>
-#include <stdexcept>
-
-//
-// There are three configuration options for this GSL implementation's behavior
-// when pre/post conditions on the GSL types are violated:
-//
-// 1. GSL_TERMINATE_ON_CONTRACT_VIOLATION: std::terminate will be called (default)
-// 2. GSL_THROW_ON_CONTRACT_VIOLATION: a gsl::fail_fast exception will be thrown
-// 3. GSL_UNENFORCED_ON_CONTRACT_VIOLATION: nothing happens
-//
-#if !(defined(GSL_THROW_ON_CONTRACT_VIOLATION) ^ defined(GSL_TERMINATE_ON_CONTRACT_VIOLATION) ^    \
-      defined(GSL_UNENFORCED_ON_CONTRACT_VIOLATION))
-#define GSL_TERMINATE_ON_CONTRACT_VIOLATION
-#endif
-
-#define GSL_STRINGIFY_DETAIL(x) #x
-#define GSL_STRINGIFY(x) GSL_STRINGIFY_DETAIL(x)
-
-#if defined(__clang__) || defined(__GNUC__)
-#define GSL_LIKELY(x) __builtin_expect (!!(x), 1)
-#define GSL_UNLIKELY(x) __builtin_expect (!!(x), 0)
-#else
-#define GSL_LIKELY(x) (x)
-#define GSL_UNLIKELY(x) (x)
-#endif
-
-//
-// GSL.assert: assertions
-//
-
-namespace gsl
-{
-struct fail_fast : public std::runtime_error
-{
-    explicit fail_fast(char const* const message) : std::runtime_error(message) {}
-};
-}
-
-#if defined(GSL_THROW_ON_CONTRACT_VIOLATION)
-
-#define Expects(cond)                                                                              \
-    if (GSL_UNLIKELY(!(cond)))                                                                     \
-        throw gsl::fail_fast("GSL: Precondition failure at " __FILE__ ": " GSL_STRINGIFY(__LINE__));
-#define Ensures(cond)                                                                              \
-    if (GSL_UNLIKELY(!(cond)))                                                                     \
-        throw gsl::fail_fast("GSL: Postcondition failure at " __FILE__                             \
-                             ": " GSL_STRINGIFY(__LINE__));
-
-#elif defined(GSL_TERMINATE_ON_CONTRACT_VIOLATION)
-
-#define Expects(cond)                                                                              \
-    if (GSL_UNLIKELY(!(cond))) std::terminate();
-#define Ensures(cond)                                                                              \
-    if (GSL_UNLIKELY(!(cond))) std::terminate();
-
-#elif defined(GSL_UNENFORCED_ON_CONTRACT_VIOLATION)
-
-#define Expects(cond)
-#define Ensures(cond)
-
-#endif
-
-#endif // GSL_CONTRACTS_H
diff --git a/include/gsl/gsl b/include/gsl/gsl
new file mode 100644
index 0000000..4cb03c5
--- /dev/null
+++ b/include/gsl/gsl
@@ -0,0 +1,190 @@
+///////////////////////////////////////////////////////////////////////////////
+//
+// Copyright (c) 2015 Microsoft Corporation. All rights reserved.
+//
+// This code is licensed under the MIT License (MIT).
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+// THE SOFTWARE.
+//
+///////////////////////////////////////////////////////////////////////////////
+
+#pragma once
+
+#ifndef GSL_GSL_H
+#define GSL_GSL_H
+
+#include <gsl/gsl_assert>  // Ensures/Expects
+#include <gsl/gsl_util>    // finally()/narrow()/narrow_cast()...
+#include <gsl/multi_span>  // multi_span, strided_span...
+#include <gsl/span>        // span
+#include <gsl/string_span> // zstring, string_span, zstring_builder...
+
+#include <iostream>
+#include <memory>
+#include <type_traits>
+
+#if defined(_MSC_VER) && _MSC_VER < 1910
+#pragma push_macro("constexpr")
+#define constexpr /*constexpr*/
+
+#endif                          // defined(_MSC_VER) && _MSC_VER < 1910
+
+namespace gsl
+{
+
+//
+// GSL.owner: ownership pointers
+//
+using std::unique_ptr;
+using std::shared_ptr;
+
+//
+// owner
+//
+// owner<T> is designed as a bridge for code that must deal directly with owning pointers for some reason
+//
+// T must be a pointer type
+// - disallow construction from any type other than pointer type
+//
+template <class T, class = std::enable_if_t<std::is_pointer<T>::value>>
+using owner = T;
+
+//
+// not_null
+//
+// Restricts a pointer or smart pointer to only hold non-null values.
+//
+// Has zero size overhead over T.
+//
+// If T is a pointer (i.e. T == U*) then
+// - allow construction from U*
+// - disallow construction from nullptr_t
+// - disallow default construction
+// - ensure construction from null U* fails
+// - allow implicit conversion to U*
+//
+template <class T>
+class not_null
+{
+public:
+    static_assert(std::is_assignable<T&, std::nullptr_t>::value, "T cannot be assigned nullptr.");
+
+    template <typename U, typename Dummy = std::enable_if_t<std::is_convertible<U, T>::value>>
+    constexpr not_null(U&& u) : ptr_(std::forward<U>(u))
+    {
+        Expects(ptr_ != nullptr);
+    }
+
+    template <typename U, typename Dummy = std::enable_if_t<std::is_convertible<U, T>::value>>
+    constexpr not_null(const not_null<U>& other) : not_null(other.get())
+    {
+    }
+
+    not_null(const not_null& other) = default;
+    not_null& operator=(const not_null& other) = default;
+
+    constexpr T get() const
+    {
+        Ensures(ptr_ != nullptr);
+        return ptr_;
+    }
+
+    constexpr operator T() const { return get(); }
+    constexpr T operator->() const { return get(); }
+    constexpr decltype(auto) operator*() const { return *get(); } 
+
+    // prevents compilation when someone attempts to assign a null pointer constant
+    not_null(std::nullptr_t) = delete;
+    not_null& operator=(std::nullptr_t) = delete;
+
+    // unwanted operators...pointers only point to single objects!
+    not_null& operator++() = delete;
+    not_null& operator--() = delete;
+    not_null operator++(int) = delete;
+    not_null operator--(int) = delete;
+    not_null& operator+=(std::ptrdiff_t) = delete;
+    not_null& operator-=(std::ptrdiff_t) = delete;
+    void operator[](std::ptrdiff_t) const = delete;
+
+private:
+    T ptr_;
+};
+
+template <class T>
+std::ostream& operator<<(std::ostream& os, const not_null<T>& val)
+{
+    os << val.get();
+    return os;
+}
+
+template <class T, class U>
+auto operator==(const not_null<T>& lhs, const not_null<U>& rhs) -> decltype(lhs.get() == rhs.get())
+{
+    return lhs.get() == rhs.get();
+}
+
+template <class T, class U>
+auto operator!=(const not_null<T>& lhs, const not_null<U>& rhs) -> decltype(lhs.get() != rhs.get())
+{
+    return lhs.get() != rhs.get();
+}
+
+template <class T, class U>
+auto operator<(const not_null<T>& lhs, const not_null<U>& rhs) -> decltype(lhs.get() < rhs.get())
+{
+    return lhs.get() < rhs.get();
+}
+
+template <class T, class U>
+auto operator<=(const not_null<T>& lhs, const not_null<U>& rhs) -> decltype(lhs.get() <= rhs.get())
+{
+    return lhs.get() <= rhs.get();
+}
+
+template <class T, class U>
+auto operator>(const not_null<T>& lhs, const not_null<U>& rhs) -> decltype(lhs.get() > rhs.get())
+{
+    return lhs.get() > rhs.get();
+}
+
+template <class T, class U>
+auto operator>=(const not_null<T>& lhs, const not_null<U>& rhs) -> decltype(lhs.get() >= rhs.get())
+{
+    return lhs.get() >= rhs.get();
+}
+
+// more unwanted operators
+template <class T, class U>
+std::ptrdiff_t operator-(const not_null<T>&, const not_null<U>&) = delete;
+template <class T>
+not_null<T> operator-(const not_null<T>&, std::ptrdiff_t) = delete;
+template <class T>
+not_null<T> operator+(const not_null<T>&, std::ptrdiff_t) = delete;
+template <class T>
+not_null<T> operator+(std::ptrdiff_t, const not_null<T>&) = delete;
+
+} // namespace gsl
+
+namespace std
+{
+template <class T>
+struct hash<gsl::not_null<T>>
+{
+    std::size_t operator()(const gsl::not_null<T>& value) const { return hash<T>{}(value); }
+};
+
+} // namespace std
+
+#if defined(_MSC_VER) && _MSC_VER < 1910
+#undef constexpr
+#pragma pop_macro("constexpr")
+
+#endif // defined(_MSC_VER) && _MSC_VER < 1910
+
+#endif // GSL_GSL_H
diff --git a/include/gsl/gsl_algorithm b/include/gsl/gsl_algorithm
new file mode 100644
index 0000000..6e81e50
--- /dev/null
+++ b/include/gsl/gsl_algorithm
@@ -0,0 +1,61 @@
+///////////////////////////////////////////////////////////////////////////////
+//
+// Copyright (c) 2015 Microsoft Corporation. All rights reserved.
+//
+// This code is licensed under the MIT License (MIT).
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+// THE SOFTWARE.
+//
+///////////////////////////////////////////////////////////////////////////////
+
+#pragma once
+
+#ifndef GSL_ALGORITHM_H
+#define GSL_ALGORITHM_H
+
+#include <gsl/span>
+
+#include <algorithm>
+
+#ifdef _MSC_VER
+#pragma warning(push)
+
+// turn off some warnings that are noisy about our Expects statements
+#pragma warning(disable : 4127) // conditional expression is constant
+
+// blanket turn off warnings from CppCoreCheck for now
+// so people aren't annoyed by them when running the tool.
+// more targeted suppressions will be added in a future update to the GSL
+#pragma warning(disable : 26481 26482 26483 26485 26490 26491 26492 26493 26495)
+#endif // _MSC_VER
+
+namespace gsl
+{
+
+template <class SrcElementType, std::ptrdiff_t SrcExtent, class DestElementType,
+          std::ptrdiff_t DestExtent>
+void copy(span<SrcElementType, SrcExtent> src, span<DestElementType, DestExtent> dest)
+{
+    static_assert(std::is_assignable<decltype(*dest.data()), decltype(*src.data())>::value,
+                  "Elements of source span can not be assigned to elements of destination span");
+    static_assert(SrcExtent == dynamic_extent || DestExtent == dynamic_extent ||
+                      (SrcExtent <= DestExtent),
+                  "Source range is longer than target range");
+
+    Expects(dest.size() >= src.size());
+    std::copy_n(src.data(), src.size(), dest.data());
+}
+
+} // namespace gsl
+
+#ifdef _MSC_VER
+#pragma warning(pop)
+#endif // _MSC_VER
+
+#endif // GSL_ALGORITHM_H
diff --git a/include/gsl/gsl_assert b/include/gsl/gsl_assert
new file mode 100644
index 0000000..43da231
--- /dev/null
+++ b/include/gsl/gsl_assert
@@ -0,0 +1,97 @@
+///////////////////////////////////////////////////////////////////////////////
+//
+// Copyright (c) 2015 Microsoft Corporation. All rights reserved.
+//
+// This code is licensed under the MIT License (MIT).
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+// THE SOFTWARE.
+//
+///////////////////////////////////////////////////////////////////////////////
+
+#pragma once
+
+#ifndef GSL_CONTRACTS_H
+#define GSL_CONTRACTS_H
+
+#include <exception>
+#include <stdexcept>
+
+//
+// There are three configuration options for this GSL implementation's behavior
+// when pre/post conditions on the GSL types are violated:
+//
+// 1. GSL_TERMINATE_ON_CONTRACT_VIOLATION: std::terminate will be called (default)
+// 2. GSL_THROW_ON_CONTRACT_VIOLATION: a gsl::fail_fast exception will be thrown
+// 3. GSL_UNENFORCED_ON_CONTRACT_VIOLATION: nothing happens
+//
+#if !(defined(GSL_THROW_ON_CONTRACT_VIOLATION) || defined(GSL_TERMINATE_ON_CONTRACT_VIOLATION) ||  \
+      defined(GSL_UNENFORCED_ON_CONTRACT_VIOLATION))
+#define GSL_TERMINATE_ON_CONTRACT_VIOLATION
+#endif
+
+#define GSL_STRINGIFY_DETAIL(x) #x
+#define GSL_STRINGIFY(x) GSL_STRINGIFY_DETAIL(x)
+
+#if defined(__clang__) || defined(__GNUC__)
+#define GSL_LIKELY(x) __builtin_expect(!!(x), 1)
+#define GSL_UNLIKELY(x) __builtin_expect(!!(x), 0)
+#else
+#define GSL_LIKELY(x) (!!(x))
+#define GSL_UNLIKELY(x) (!!(x))
+#endif
+
+//
+// GSL_ASSUME(cond)
+//
+// Tell the optimizer that the predicate cond must hold. It is unspecified
+// whether or not cond is actually evaluated.
+//
+#ifdef _MSC_VER
+#define GSL_ASSUME(cond) __assume(cond)
+#elif defined(__clang__)
+#define GSL_ASSUME(cond) __builtin_assume(cond)
+#elif defined(__GNUC__)
+#define GSL_ASSUME(cond) ((cond) ? static_cast<void>(0) : __builtin_unreachable())
+#else
+#define GSL_ASSUME(cond) static_cast<void>(!!(cond))
+#endif
+
+//
+// GSL.assert: assertions
+//
+
+namespace gsl
+{
+struct fail_fast : public std::logic_error
+{
+    explicit fail_fast(char const* const message) : std::logic_error(message) {}
+};
+}
+
+#if defined(GSL_THROW_ON_CONTRACT_VIOLATION)
+
+#define GSL_CONTRACT_CHECK(type, cond)                                                             \
+    (GSL_LIKELY(cond) ? static_cast<void>(0)                                                       \
+                      : throw gsl::fail_fast("GSL: " type " failure at " __FILE__                  \
+                                             ": " GSL_STRINGIFY(__LINE__)))
+
+#elif defined(GSL_TERMINATE_ON_CONTRACT_VIOLATION)
+
+#define GSL_CONTRACT_CHECK(type, cond) (GSL_LIKELY(cond) ? static_cast<void>(0) : std::terminate())
+
+#elif defined(GSL_UNENFORCED_ON_CONTRACT_VIOLATION)
+
+#define GSL_CONTRACT_CHECK(type, cond) GSL_ASSUME(cond)
+
+#endif
+
+#define Expects(cond) GSL_CONTRACT_CHECK("Precondition", cond)
+#define Ensures(cond) GSL_CONTRACT_CHECK("Postcondition", cond)
+
+#endif // GSL_CONTRACTS_H
diff --git a/gsl/gsl_byte b/include/gsl/gsl_byte
similarity index 71%
rename from gsl/gsl_byte
rename to include/gsl/gsl_byte
index 3f77923..1e2e3be 100644
--- a/gsl/gsl_byte
+++ b/include/gsl/gsl_byte
@@ -19,6 +19,8 @@
 #ifndef GSL_BYTE_H
 #define GSL_BYTE_H
 
+#include <type_traits>
+
 #ifdef _MSC_VER
 
 #pragma warning(push)
@@ -26,23 +28,47 @@
 // don't warn about function style casts in byte related operators
 #pragma warning(disable : 26493)
 
-// MSVC 2013 workarounds
-#if _MSC_VER <= 1800
+#ifndef GSL_USE_STD_BYTE
+// this tests if we are under MSVC and the standard lib has std::byte and it is enabled
+#if _MSC_VER >= 1911 && (!defined(_HAS_STD_BYTE) || _HAS_STD_BYTE)
 
-// constexpr is not understood
-#pragma push_macro("constexpr")
-#define constexpr /*constexpr*/
+#define GSL_USE_STD_BYTE 1
 
-// noexcept is not understood
-#pragma push_macro("noexcept")
-#define noexcept /*noexcept*/
+#else // _MSC_VER >= 1911 && (!defined(_HAS_STD_BYTE) || _HAS_STD_BYTE)
 
-#endif // _MSC_VER <= 1800
+#define GSL_USE_STD_BYTE 0
 
-#endif // _MSC_VER
+#endif // _MSC_VER >= 1911 && (!defined(_HAS_STD_BYTE) || _HAS_STD_BYTE)
+#endif // GSL_USE_STD_BYTE
+
+#else // _MSC_VER
+
+#ifndef GSL_USE_STD_BYTE
+// this tests if we are under GCC or Clang with enough -std:c++1z power to get us std::byte
+#if defined(__cplusplus) && (__cplusplus >= 201703L)
+
+#define GSL_USE_STD_BYTE 1
+#include <cstddef>
+
+#else // defined(__cplusplus) && (__cplusplus >= 201703L)
+
+#define GSL_USE_STD_BYTE 0
+
+#endif //defined(__cplusplus) && (__cplusplus >= 201703L)
+#endif // GSL_USE_STD_BYTE
+
+#endif           // _MSC_VER
 
 namespace gsl
 {
+#if GSL_USE_STD_BYTE
+
+
+using std::byte;
+using std::to_integer;
+
+#else // GSL_USE_STD_BYTE
+
 // This is a simple definition for now that allows
 // use of byte within span<> to be standards-compliant
 enum class byte : unsigned char
@@ -111,51 +137,40 @@
     return static_cast<IntegerType>(b);
 }
 
-template<bool E, typename T>
+#endif // GSL_USE_STD_BYTE
+
+template <bool E, typename T>
 inline constexpr byte to_byte_impl(T t) noexcept
 {
     static_assert(
-        E,
-        "gsl::to_byte(t) must be provided an unsigned char, otherwise data loss may occur. "
-        "If you are calling to_byte with an integer contant use: gsl::to_byte<t>() version."
-    );
+        E, "gsl::to_byte(t) must be provided an unsigned char, otherwise data loss may occur. "
+           "If you are calling to_byte with an integer contant use: gsl::to_byte<t>() version.");
     return static_cast<byte>(t);
 }
-template<>
+template <>
 inline constexpr byte to_byte_impl<true, unsigned char>(unsigned char t) noexcept
 {
-     return byte(t);
+    return byte(t);
 }
 
-template<typename T>
+template <typename T>
 inline constexpr byte to_byte(T t) noexcept
 {
-     return to_byte_impl<std::is_same<T, unsigned char>::value, T>(t);
+    return to_byte_impl<std::is_same<T, unsigned char>::value, T>(t);
 }
 
 template <int I>
 inline constexpr byte to_byte() noexcept
 {
-    static_assert(I >= 0 && I <= 255, "gsl::byte only has 8 bits of storage, values must be in range 0-255");
+    static_assert(I >= 0 && I <= 255,
+                  "gsl::byte only has 8 bits of storage, values must be in range 0-255");
     return static_cast<byte>(I);
 }
 
 } // namespace gsl
 
 #ifdef _MSC_VER
-
-#if _MSC_VER <= 1800
-
-#undef constexpr
-#pragma pop_macro("constexpr")
-
-#undef noexcept
-#pragma pop_macro("noexcept")
-
-#endif // _MSC_VER <= 1800
-
 #pragma warning(pop)
-
 #endif // _MSC_VER
 
 #endif // GSL_BYTE_H
diff --git a/gsl/gsl_util b/include/gsl/gsl_util
similarity index 74%
rename from gsl/gsl_util
rename to include/gsl/gsl_util
index a8a41ae..60ef498 100644
--- a/gsl/gsl_util
+++ b/include/gsl/gsl_util
@@ -19,34 +19,23 @@
 #ifndef GSL_UTIL_H
 #define GSL_UTIL_H
 
-#include "gsl_assert" // Ensures/Expects
+#include <gsl/gsl_assert> // Ensures/Expects
+
 #include <array>
 #include <exception>
 #include <type_traits>
 #include <utility>
 
-#ifdef _MSC_VER
-
-// No MSVC does constexpr fully yet
-#pragma push_macro("constexpr")
-#define constexpr /*constexpr*/
+#if defined(_MSC_VER)
 
 #pragma warning(push)
 #pragma warning(disable : 4127) // conditional expression is constant
 
-// MSVC 2013 workarounds
-#if _MSC_VER <= 1800
-// noexcept is not understood
-#pragma push_macro("noexcept")
-#define noexcept /*noexcept*/
-
-// turn off some misguided warnings
-#pragma warning(push)
-#pragma warning(disable : 4351) // warns about newly introduced aggregate initializer behavior
-
-#endif // _MSC_VER <= 1800
-
-#endif // _MSC_VER
+#if _MSC_VER < 1910
+#pragma push_macro("constexpr")
+#define constexpr /*constexpr*/
+#endif                          // _MSC_VER < 1910
+#endif                          // _MSC_VER
 
 // AFAIK there's no way to detect in GCC or MSVC. GCC has an __EXCEPTIONS macro,
 // but that is only defined if `-fexceptions` is *explicitly* passed on the
@@ -105,19 +94,11 @@
 }
 
 // narrow_cast(): a searchable way to do narrowing casts of values
-#if _MSC_VER <= 1800
-template <class T, class U>
-inline constexpr T narrow_cast(U u) noexcept
-{
-    return static_cast<T>(u);
-}
-#else
 template <class T, class U>
 inline constexpr T narrow_cast(U&& u) noexcept
 {
     return static_cast<T>(std::forward<U>(u));
 }
-#endif
 
 struct narrowing_error : public std::exception
 {
@@ -149,31 +130,25 @@
 }
 
 //
-// at() - Bounds-checked way of accessing static arrays, std::array, std::vector
+// at() - Bounds-checked way of accessing builtin arrays, std::array, std::vector
 //
-template <class T, size_t N>
-constexpr T& at(T (&arr)[N], std::ptrdiff_t index)
+template <class T, std::size_t N>
+inline constexpr T& at(T (&arr)[N], const std::ptrdiff_t index)
 {
     Expects(index >= 0 && index < narrow_cast<std::ptrdiff_t>(N));
-    return arr[static_cast<size_t>(index)];
-}
-
-template <class T, size_t N>
-constexpr T& at(std::array<T, N>& arr, std::ptrdiff_t index)
-{
-    Expects(index >= 0 && index < narrow_cast<std::ptrdiff_t>(N));
-    return arr[static_cast<size_t>(index)];
+    return arr[static_cast<std::size_t>(index)];
 }
 
 template <class Cont>
-constexpr typename Cont::value_type& at(Cont& cont, std::ptrdiff_t index)
+inline constexpr auto at(Cont& cont, const std::ptrdiff_t index) -> decltype(cont[cont.size()])
 {
     Expects(index >= 0 && index < narrow_cast<std::ptrdiff_t>(cont.size()));
-    return cont[static_cast<typename Cont::size_type>(index)];
+    using size_type = decltype(cont.size());
+    return cont[static_cast<size_type>(index)];
 }
 
 template <class T>
-constexpr const T& at(std::initializer_list<T> cont, std::ptrdiff_t index)
+inline constexpr T at(const std::initializer_list<T> cont, const std::ptrdiff_t index)
 {
     Expects(index >= 0 && index < narrow_cast<std::ptrdiff_t>(cont.size()));
     return *(cont.begin() + index);
@@ -181,22 +156,15 @@
 
 } // namespace gsl
 
-#ifdef _MSC_VER
-
-#pragma warning(pop)
-
+#if defined(_MSC_VER)
+#if _MSC_VER < 1910
 #undef constexpr
 #pragma pop_macro("constexpr")
 
-#if _MSC_VER <= 1800
-
-#undef noexcept
-#pragma pop_macro("noexcept")
+#endif // _MSC_VER < 1910
 
 #pragma warning(pop)
 
-#endif // _MSC_VER <= 1800
-
 #endif // _MSC_VER
 
 #endif // GSL_UTIL_H
diff --git a/gsl/multi_span b/include/gsl/multi_span
similarity index 76%
rename from gsl/multi_span
rename to include/gsl/multi_span
index 2186c7b..5e0eaac 100644
--- a/gsl/multi_span
+++ b/include/gsl/multi_span
@@ -19,9 +19,10 @@
 #ifndef GSL_MULTI_SPAN_H
 #define GSL_MULTI_SPAN_H
 
-#include "gsl_assert"
-#include "gsl_byte"
-#include "gsl_util"
+#include <gsl/gsl_assert>
+#include <gsl/gsl_byte>
+#include <gsl/gsl_util>
+
 #include <algorithm>
 #include <array>
 #include <cassert>
@@ -41,40 +42,19 @@
 // turn off some warnings that are noisy about our Expects statements
 #pragma warning(push)
 #pragma warning(disable : 4127) // conditional expression is constant
+#pragma warning(disable : 4702) // unreachable code
 
-// No MSVC does constexpr fully yet
+#if _MSC_VER < 1910
 #pragma push_macro("constexpr")
 #define constexpr /*constexpr*/
 
-// VS 2013 workarounds
-#if _MSC_VER <= 1800
-
-#define GSL_MSVC_HAS_VARIADIC_CTOR_BUG
-#define GSL_MSVC_NO_SUPPORT_FOR_MOVE_CTOR_DEFAULT
-
-// noexcept is not understood
-#ifndef GSL_THROW_ON_CONTRACT_VIOLATION
-#pragma push_macro("noexcept")
-#define noexcept /*noexcept*/
-#endif
-
-// turn off some misguided warnings
-#pragma warning(push)
-#pragma warning(disable : 4351) // warns about newly introduced aggregate initializer behavior
-#pragma warning(disable : 4512) // warns that assignment op could not be generated
-
-#endif // _MSC_VER <= 1800
-
-#endif // _MSC_VER
+#endif                          // _MSC_VER < 1910
+#endif                          // _MSC_VER
 
 #ifdef GSL_THROW_ON_CONTRACT_VIOLATION
-
-#ifdef _MSC_VER
-#pragma push_macro("noexcept")
-#endif
-
-#define noexcept /*noexcept*/
-
+#define GSL_NOEXCEPT /*noexcept*/
+#else
+#define GSL_NOEXCEPT noexcept
 #endif // GSL_THROW_ON_CONTRACT_VIOLATION
 
 namespace gsl
@@ -104,129 +84,121 @@
     };
 }
 
-template <size_t Rank>
+template <std::size_t Rank>
 class index final
 {
     static_assert(Rank > 0, "Rank must be greater than 0!");
 
-    template <size_t OtherRank>
+    template <std::size_t OtherRank>
     friend class index;
 
 public:
-    static const size_t rank = Rank;
+    static const std::size_t rank = Rank;
     using value_type = std::ptrdiff_t;
     using size_type = value_type;
     using reference = std::add_lvalue_reference_t<value_type>;
     using const_reference = std::add_lvalue_reference_t<std::add_const_t<value_type>>;
 
-    constexpr index() noexcept {}
+    constexpr index() GSL_NOEXCEPT {}
 
-    constexpr index(const value_type (&values)[Rank]) noexcept
+    constexpr index(const value_type (&values)[Rank]) GSL_NOEXCEPT
     {
         std::copy(values, values + Rank, elems);
     }
 
-#ifdef GSL_MSVC_HAS_VARIADIC_CTOR_BUG
-    template <
-        typename T, typename... Ts,
-        typename = std::enable_if_t<((sizeof...(Ts) + 1) == Rank) && std::is_integral<T>::value &&
-                                    details::are_integral<Ts...>::value>>
-    constexpr index(T t, Ts... ds)
-        : index({narrow_cast<value_type>(t), narrow_cast<value_type>(ds)...})
-    {
-    }
-#else
     template <typename... Ts, typename = std::enable_if_t<(sizeof...(Ts) == Rank) &&
                                                           details::are_integral<Ts...>::value>>
-    constexpr index(Ts... ds) noexcept : elems{narrow_cast<value_type>(ds)...}
+    constexpr index(Ts... ds) GSL_NOEXCEPT : elems{narrow_cast<value_type>(ds)...}
     {
     }
-#endif
 
-    constexpr index(const index& other) noexcept = default;
+    constexpr index(const index& other) GSL_NOEXCEPT = default;
 
-    constexpr index& operator=(const index& rhs) noexcept = default;
+    constexpr index& operator=(const index& rhs) GSL_NOEXCEPT = default;
 
     // Preconditions: component_idx < rank
-    constexpr reference operator[](size_t component_idx)
+    constexpr reference operator[](std::size_t component_idx)
     {
         Expects(component_idx < Rank); // Component index must be less than rank
         return elems[component_idx];
     }
 
     // Preconditions: component_idx < rank
-    constexpr const_reference operator[](size_t component_idx) const noexcept
+    constexpr const_reference operator[](std::size_t component_idx) const GSL_NOEXCEPT
     {
         Expects(component_idx < Rank); // Component index must be less than rank
         return elems[component_idx];
     }
 
-    constexpr bool operator==(const index& rhs) const noexcept
+    constexpr bool operator==(const index& rhs) const GSL_NOEXCEPT
     {
         return std::equal(elems, elems + rank, rhs.elems);
     }
 
-    constexpr bool operator!=(const index& rhs) const noexcept { return !(this == rhs); }
+    constexpr bool operator!=(const index& rhs) const GSL_NOEXCEPT { return !(this == rhs); }
 
-    constexpr index operator+() const noexcept { return *this; }
+    constexpr index operator+() const GSL_NOEXCEPT { return *this; }
 
-    constexpr index operator-() const noexcept
+    constexpr index operator-() const GSL_NOEXCEPT
     {
         index ret = *this;
         std::transform(ret, ret + rank, ret, std::negate<value_type>{});
         return ret;
     }
 
-    constexpr index operator+(const index& rhs) const noexcept
+    constexpr index operator+(const index& rhs) const GSL_NOEXCEPT
     {
         index ret = *this;
         ret += rhs;
         return ret;
     }
 
-    constexpr index operator-(const index& rhs) const noexcept
+    constexpr index operator-(const index& rhs) const GSL_NOEXCEPT
     {
         index ret = *this;
         ret -= rhs;
         return ret;
     }
 
-    constexpr index& operator+=(const index& rhs) noexcept
+    constexpr index& operator+=(const index& rhs) GSL_NOEXCEPT
     {
         std::transform(elems, elems + rank, rhs.elems, elems, std::plus<value_type>{});
         return *this;
     }
 
-    constexpr index& operator-=(const index& rhs) noexcept
+    constexpr index& operator-=(const index& rhs) GSL_NOEXCEPT
     {
         std::transform(elems, elems + rank, rhs.elems, elems, std::minus<value_type>{});
         return *this;
     }
 
-    constexpr index operator*(value_type v) const noexcept
+    constexpr index operator*(value_type v) const GSL_NOEXCEPT
     {
         index ret = *this;
         ret *= v;
         return ret;
     }
 
-    constexpr index operator/(value_type v) const noexcept
+    constexpr index operator/(value_type v) const GSL_NOEXCEPT
     {
         index ret = *this;
         ret /= v;
         return ret;
     }
 
-    friend constexpr index operator*(value_type v, const index& rhs) noexcept { return rhs * v; }
+    friend constexpr index operator*(value_type v, const index& rhs) GSL_NOEXCEPT
+    {
+        return rhs * v;
+    }
 
-    constexpr index& operator*=(value_type v) noexcept
+    constexpr index& operator*=(value_type v) GSL_NOEXCEPT
     {
         std::transform(elems, elems + rank, elems,
                        [v](value_type x) { return std::multiplies<value_type>{}(x, v); });
         return *this;
     }
 
-    constexpr index& operator/=(value_type v) noexcept
+    constexpr index& operator/=(value_type v) GSL_NOEXCEPT
     {
         std::transform(elems, elems + rank, elems,
                        [v](value_type x) { return std::divides<value_type>{}(x, v); });
@@ -237,37 +209,47 @@
     value_type elems[Rank] = {};
 };
 
-#ifndef _MSC_VER
+#if !defined(_MSC_VER) || _MSC_VER >= 1910
 
 struct static_bounds_dynamic_range_t
 {
     template <typename T, typename Dummy = std::enable_if_t<std::is_integral<T>::value>>
-    constexpr operator T() const noexcept
+    constexpr operator T() const GSL_NOEXCEPT
     {
         return narrow_cast<T>(-1);
     }
-
-    template <typename T, typename Dummy = std::enable_if_t<std::is_integral<T>::value>>
-    constexpr bool operator==(T other) const noexcept
-    {
-        return narrow_cast<T>(-1) == other;
-    }
-
-    template <typename T, typename Dummy = std::enable_if_t<std::is_integral<T>::value>>
-    constexpr bool operator!=(T other) const noexcept
-    {
-        return narrow_cast<T>(-1) != other;
-    }
 };
 
+constexpr bool operator==(static_bounds_dynamic_range_t, static_bounds_dynamic_range_t) GSL_NOEXCEPT
+{
+    return true;
+}
+
+constexpr bool operator!=(static_bounds_dynamic_range_t, static_bounds_dynamic_range_t) GSL_NOEXCEPT
+{
+    return false;
+}
+
 template <typename T, typename Dummy = std::enable_if_t<std::is_integral<T>::value>>
-constexpr bool operator==(T left, static_bounds_dynamic_range_t right) noexcept
+constexpr bool operator==(static_bounds_dynamic_range_t, T other) GSL_NOEXCEPT
+{
+    return narrow_cast<T>(-1) == other;
+}
+
+template <typename T, typename Dummy = std::enable_if_t<std::is_integral<T>::value>>
+constexpr bool operator==(T left, static_bounds_dynamic_range_t right) GSL_NOEXCEPT
 {
     return right == left;
 }
 
 template <typename T, typename Dummy = std::enable_if_t<std::is_integral<T>::value>>
-constexpr bool operator!=(T left, static_bounds_dynamic_range_t right) noexcept
+constexpr bool operator!=(static_bounds_dynamic_range_t, T other) GSL_NOEXCEPT
+{
+    return narrow_cast<T>(-1) != other;
+}
+
+template <typename T, typename Dummy = std::enable_if_t<std::is_integral<T>::value>>
+constexpr bool operator!=(T left, static_bounds_dynamic_range_t right) GSL_NOEXCEPT
 {
     return right != left;
 }
@@ -311,28 +293,28 @@
         BoundsRanges(const std::ptrdiff_t* const) {}
         BoundsRanges() = default;
 
-        template <typename T, size_t Dim>
+        template <typename T, std::size_t Dim>
         void serialize(T&) const
         {
         }
 
-        template <typename T, size_t Dim>
+        template <typename T, std::size_t Dim>
         size_type linearize(const T&) const
         {
             return 0;
         }
 
-        template <typename T, size_t Dim>
+        template <typename T, std::size_t Dim>
         size_type contains(const T&) const
         {
             return -1;
         }
 
-        size_type elementNum(size_t) const noexcept { return 0; }
+        size_type elementNum(std::size_t) const GSL_NOEXCEPT { return 0; }
 
-        size_type totalSize() const noexcept { return TotalSize; }
+        size_type totalSize() const GSL_NOEXCEPT { return TotalSize; }
 
-        bool operator==(const BoundsRanges&) const noexcept { return true; }
+        bool operator==(const BoundsRanges&) const GSL_NOEXCEPT { return true; }
     };
 
     template <std::ptrdiff_t... RestRanges>
@@ -340,14 +322,15 @@
     {
         using Base = BoundsRanges<RestRanges...>;
         using size_type = std::ptrdiff_t;
-        static const size_t Depth = Base::Depth + 1;
-        static const size_t DynamicNum = Base::DynamicNum + 1;
+        static const std::size_t Depth = Base::Depth + 1;
+        static const std::size_t DynamicNum = Base::DynamicNum + 1;
         static const size_type CurrentRange = dynamic_range;
         static const size_type TotalSize = dynamic_range;
+
     private:
         size_type m_bound;
-    public:
 
+    public:
         BoundsRanges(const std::ptrdiff_t* const arr)
             : Base(arr + 1), m_bound(*arr * this->Base::totalSize())
         {
@@ -364,14 +347,14 @@
         {
         }
 
-        template <typename T, size_t Dim = 0>
+        template <typename T, std::size_t Dim = 0>
         void serialize(T& arr) const
         {
             arr[Dim] = elementNum();
             this->Base::template serialize<T, Dim + 1>(arr);
         }
 
-        template <typename T, size_t Dim = 0>
+        template <typename T, std::size_t Dim = 0>
         size_type linearize(const T& arr) const
         {
             const size_type index = this->Base::totalSize() * arr[Dim];
@@ -379,7 +362,7 @@
             return index + this->Base::template linearize<T, Dim + 1>(arr);
         }
 
-        template <typename T, size_t Dim = 0>
+        template <typename T, std::size_t Dim = 0>
         size_type contains(const T& arr) const
         {
             const ptrdiff_t last = this->Base::template contains<T, Dim + 1>(arr);
@@ -388,11 +371,11 @@
             return cur < m_bound ? cur + last : -1;
         }
 
-        size_type totalSize() const noexcept { return m_bound; }
+        size_type totalSize() const GSL_NOEXCEPT { return m_bound; }
 
-        size_type elementNum() const noexcept { return totalSize() / this->Base::totalSize(); }
+        size_type elementNum() const GSL_NOEXCEPT { return totalSize() / this->Base::totalSize(); }
 
-        size_type elementNum(size_t dim) const noexcept
+        size_type elementNum(std::size_t dim) const GSL_NOEXCEPT
         {
             if (dim > 0)
                 return this->Base::elementNum(dim - 1);
@@ -400,7 +383,7 @@
                 return elementNum();
         }
 
-        bool operator==(const BoundsRanges& rhs) const noexcept
+        bool operator==(const BoundsRanges& rhs) const GSL_NOEXCEPT
         {
             return m_bound == rhs.m_bound &&
                    static_cast<const Base&>(*this) == static_cast<const Base&>(rhs);
@@ -412,8 +395,8 @@
     {
         using Base = BoundsRanges<RestRanges...>;
         using size_type = std::ptrdiff_t;
-        static const size_t Depth = Base::Depth + 1;
-        static const size_t DynamicNum = Base::DynamicNum;
+        static const std::size_t Depth = Base::Depth + 1;
+        static const std::size_t DynamicNum = Base::DynamicNum;
         static const size_type CurrentRange = CurRange;
         static const size_type TotalSize =
             Base::TotalSize == dynamic_range ? dynamic_range : CurrentRange * Base::TotalSize;
@@ -429,14 +412,14 @@
             (void) firstLevel;
         }
 
-        template <typename T, size_t Dim = 0>
+        template <typename T, std::size_t Dim = 0>
         void serialize(T& arr) const
         {
             arr[Dim] = elementNum();
             this->Base::template serialize<T, Dim + 1>(arr);
         }
 
-        template <typename T, size_t Dim = 0>
+        template <typename T, std::size_t Dim = 0>
         size_type linearize(const T& arr) const
         {
             Expects(arr[Dim] >= 0 && arr[Dim] < CurrentRange); // Index is out of range
@@ -444,7 +427,7 @@
                    this->Base::template linearize<T, Dim + 1>(arr);
         }
 
-        template <typename T, size_t Dim = 0>
+        template <typename T, std::size_t Dim = 0>
         size_type contains(const T& arr) const
         {
             if (arr[Dim] >= CurrentRange) return -1;
@@ -453,11 +436,11 @@
             return this->Base::totalSize() * arr[Dim] + last;
         }
 
-        size_type totalSize() const noexcept { return CurrentRange * this->Base::totalSize(); }
+        size_type totalSize() const GSL_NOEXCEPT { return CurrentRange * this->Base::totalSize(); }
 
-        size_type elementNum() const noexcept { return CurrentRange; }
+        size_type elementNum() const GSL_NOEXCEPT { return CurrentRange; }
 
-        size_type elementNum(size_t dim) const noexcept
+        size_type elementNum(std::size_t dim) const GSL_NOEXCEPT
         {
             if (dim > 0)
                 return this->Base::elementNum(dim - 1);
@@ -465,7 +448,7 @@
                 return elementNum();
         }
 
-        bool operator==(const BoundsRanges& rhs) const noexcept
+        bool operator==(const BoundsRanges& rhs) const GSL_NOEXCEPT
         {
             return static_cast<const Base&>(*this) == static_cast<const Base&>(rhs);
         }
@@ -486,20 +469,21 @@
         const TypeChain& obj_;
         TypeListIndexer(const TypeChain& obj) : obj_(obj) {}
 
-        template <size_t N>
+        template <std::size_t N>
         const TypeChain& getObj(std::true_type)
         {
             return obj_;
         }
 
-        template <size_t N, typename MyChain = TypeChain, typename MyBase = typename MyChain::Base>
+        template <std::size_t N, typename MyChain = TypeChain,
+                  typename MyBase = typename MyChain::Base>
         auto getObj(std::false_type)
             -> decltype(TypeListIndexer<MyBase>(static_cast<const MyBase&>(obj_)).template get<N>())
         {
             return TypeListIndexer<MyBase>(static_cast<const MyBase&>(obj_)).template get<N>();
         }
 
-        template <size_t N>
+        template <std::size_t N>
         auto get() -> decltype(getObj<N - 1>(std::integral_constant<bool, N == 0>()))
         {
             return getObj<N - 1>(std::integral_constant<bool, N == 0>());
@@ -512,12 +496,12 @@
         return TypeListIndexer<TypeChain>(obj);
     }
 
-    template <size_t Rank, bool Enabled = (Rank > 1),
+    template <std::size_t Rank, bool Enabled = (Rank > 1),
               typename Ret = std::enable_if_t<Enabled, index<Rank - 1>>>
-    constexpr Ret shift_left(const index<Rank>& other) noexcept
+    inline constexpr Ret shift_left(const index<Rank>& other) GSL_NOEXCEPT
     {
         Ret ret{};
-        for (size_t i = 0; i < Rank - 1; ++i) {
+        for (std::size_t i = 0; i < Rank - 1; ++i) {
             ret[i] = other[i + 1];
         }
         return ret;
@@ -546,8 +530,8 @@
     friend class static_bounds;
 
 public:
-    static const size_t rank = MyRanges::Depth;
-    static const size_t dynamic_rank = MyRanges::DynamicNum;
+    static const std::size_t rank = MyRanges::Depth;
+    static const std::size_t dynamic_rank = MyRanges::DynamicNum;
     static const std::ptrdiff_t static_size = MyRanges::TotalSize;
 
     using size_type = std::ptrdiff_t;
@@ -561,18 +545,18 @@
 
     constexpr static_bounds(const static_bounds&) = default;
 
-    template <typename SourceType, typename TargetType, size_t Rank>
+    template <typename SourceType, typename TargetType, std::size_t Rank>
     struct BoundsRangeConvertible2;
 
-    template <size_t Rank, typename SourceType, typename TargetType,
+    template <std::size_t Rank, typename SourceType, typename TargetType,
               typename Ret = BoundsRangeConvertible2<typename SourceType::Base,
                                                      typename TargetType::Base, Rank>>
     static auto helpBoundsRangeConvertible(SourceType, TargetType, std::true_type) -> Ret;
 
-    template <size_t Rank, typename SourceType, typename TargetType>
+    template <std::size_t Rank, typename SourceType, typename TargetType>
     static auto helpBoundsRangeConvertible(SourceType, TargetType, ...) -> std::false_type;
 
-    template <typename SourceType, typename TargetType, size_t Rank>
+    template <typename SourceType, typename TargetType, std::size_t Rank>
     struct BoundsRangeConvertible2
         : decltype(helpBoundsRangeConvertible<Rank - 1>(
               SourceType(), TargetType(),
@@ -629,31 +613,31 @@
 
     constexpr static_bounds() = default;
 
-    constexpr sliced_type slice() const noexcept
+    constexpr sliced_type slice() const GSL_NOEXCEPT
     {
         return sliced_type{static_cast<const details::BoundsRanges<RestRanges...>&>(m_ranges)};
     }
 
-    constexpr size_type stride() const noexcept { return rank > 1 ? slice().size() : 1; }
+    constexpr size_type stride() const GSL_NOEXCEPT { return rank > 1 ? slice().size() : 1; }
 
-    constexpr size_type size() const noexcept { return m_ranges.totalSize(); }
+    constexpr size_type size() const GSL_NOEXCEPT { return m_ranges.totalSize(); }
 
-    constexpr size_type total_size() const noexcept { return m_ranges.totalSize(); }
+    constexpr size_type total_size() const GSL_NOEXCEPT { return m_ranges.totalSize(); }
 
     constexpr size_type linearize(const index_type& idx) const { return m_ranges.linearize(idx); }
 
-    constexpr bool contains(const index_type& idx) const noexcept
+    constexpr bool contains(const index_type& idx) const GSL_NOEXCEPT
     {
         return m_ranges.contains(idx) != -1;
     }
 
-    constexpr size_type operator[](size_t index) const noexcept
+    constexpr size_type operator[](std::size_t index) const GSL_NOEXCEPT
     {
         return m_ranges.elementNum(index);
     }
 
-    template <size_t Dim = 0>
-    constexpr size_type extent() const noexcept
+    template <std::size_t Dim = 0>
+    constexpr size_type extent() const GSL_NOEXCEPT
     {
         static_assert(Dim < rank,
                       "dimension should be less than rank (dimension count starts from 0)");
@@ -661,17 +645,17 @@
     }
 
     template <typename IntType>
-    constexpr size_type extent(IntType dim) const noexcept
+    constexpr size_type extent(IntType dim) const GSL_NOEXCEPT
     {
         static_assert(std::is_integral<IntType>::value,
                       "Dimension parameter must be supplied as an integral type.");
-        auto real_dim = narrow_cast<size_t>(dim);
+        auto real_dim = narrow_cast<std::size_t>(dim);
         Expects(real_dim < rank);
 
         return m_ranges.elementNum(real_dim);
     }
 
-    constexpr index_type index_bounds() const noexcept
+    constexpr index_type index_bounds() const GSL_NOEXCEPT
     {
         size_type extents[rank] = {};
         m_ranges.serialize(extents);
@@ -679,33 +663,36 @@
     }
 
     template <std::ptrdiff_t... Ranges>
-    constexpr bool operator==(const static_bounds<Ranges...>& rhs) const noexcept
+    constexpr bool operator==(const static_bounds<Ranges...>& rhs) const GSL_NOEXCEPT
     {
         return this->size() == rhs.size();
     }
 
     template <std::ptrdiff_t... Ranges>
-    constexpr bool operator!=(const static_bounds<Ranges...>& rhs) const noexcept
+    constexpr bool operator!=(const static_bounds<Ranges...>& rhs) const GSL_NOEXCEPT
     {
         return !(*this == rhs);
     }
 
-    constexpr const_iterator begin() const noexcept { return const_iterator(*this, index_type{}); }
+    constexpr const_iterator begin() const GSL_NOEXCEPT
+    {
+        return const_iterator(*this, index_type{});
+    }
 
-    constexpr const_iterator end() const noexcept
+    constexpr const_iterator end() const GSL_NOEXCEPT
     {
         return const_iterator(*this, this->index_bounds());
     }
 };
 
-template <size_t Rank>
+template <std::size_t Rank>
 class strided_bounds
 {
-    template <size_t OtherRank>
+    template <std::size_t OtherRank>
     friend class strided_bounds;
 
 public:
-    static const size_t rank = Rank;
+    static const std::size_t rank = Rank;
     using value_type = std::ptrdiff_t;
     using reference = std::add_lvalue_reference_t<value_type>;
     using const_reference = std::add_const_t<reference>;
@@ -720,60 +707,60 @@
     using sliced_type = std::conditional_t<rank != 0, strided_bounds<rank - 1>, void>;
     using mapping_type = generalized_mapping_tag;
 
-    constexpr strided_bounds(const strided_bounds&) noexcept = default;
+    constexpr strided_bounds(const strided_bounds&) GSL_NOEXCEPT = default;
 
-    constexpr strided_bounds& operator=(const strided_bounds&) noexcept = default;
+    constexpr strided_bounds& operator=(const strided_bounds&) GSL_NOEXCEPT = default;
 
     constexpr strided_bounds(const value_type (&values)[rank], index_type strides)
         : m_extents(values), m_strides(std::move(strides))
     {
     }
 
-    constexpr strided_bounds(const index_type& extents, const index_type& strides) noexcept
+    constexpr strided_bounds(const index_type& extents, const index_type& strides) GSL_NOEXCEPT
         : m_extents(extents),
           m_strides(strides)
     {
     }
 
-    constexpr index_type strides() const noexcept { return m_strides; }
+    constexpr index_type strides() const GSL_NOEXCEPT { return m_strides; }
 
-    constexpr size_type total_size() const noexcept
+    constexpr size_type total_size() const GSL_NOEXCEPT
     {
         size_type ret = 0;
-        for (size_t i = 0; i < rank; ++i) {
+        for (std::size_t i = 0; i < rank; ++i) {
             ret += (m_extents[i] - 1) * m_strides[i];
         }
         return ret + 1;
     }
 
-    constexpr size_type size() const noexcept
+    constexpr size_type size() const GSL_NOEXCEPT
     {
         size_type ret = 1;
-        for (size_t i = 0; i < rank; ++i) {
+        for (std::size_t i = 0; i < rank; ++i) {
             ret *= m_extents[i];
         }
         return ret;
     }
 
-    constexpr bool contains(const index_type& idx) const noexcept
+    constexpr bool contains(const index_type& idx) const GSL_NOEXCEPT
     {
-        for (size_t i = 0; i < rank; ++i) {
+        for (std::size_t i = 0; i < rank; ++i) {
             if (idx[i] < 0 || idx[i] >= m_extents[i]) return false;
         }
         return true;
     }
 
-    constexpr size_type linearize(const index_type& idx) const noexcept
+    constexpr size_type linearize(const index_type& idx) const GSL_NOEXCEPT
     {
         size_type ret = 0;
-        for (size_t i = 0; i < rank; i++) {
+        for (std::size_t i = 0; i < rank; i++) {
             Expects(idx[i] < m_extents[i]); // index is out of bounds of the array
             ret += idx[i] * m_strides[i];
         }
         return ret;
     }
 
-    constexpr size_type stride() const noexcept { return m_strides[0]; }
+    constexpr size_type stride() const GSL_NOEXCEPT { return m_strides[0]; }
 
     template <bool Enabled = (rank > 1), typename Ret = std::enable_if_t<Enabled, sliced_type>>
     constexpr sliced_type slice() const
@@ -781,18 +768,24 @@
         return {details::shift_left(m_extents), details::shift_left(m_strides)};
     }
 
-    template <size_t Dim = 0>
-    constexpr size_type extent() const noexcept
+    template <std::size_t Dim = 0>
+    constexpr size_type extent() const GSL_NOEXCEPT
     {
         static_assert(Dim < Rank,
                       "dimension should be less than rank (dimension count starts from 0)");
         return m_extents[Dim];
     }
 
-    constexpr index_type index_bounds() const noexcept { return m_extents; }
-    constexpr const_iterator begin() const noexcept { return const_iterator{*this, index_type{}}; }
+    constexpr index_type index_bounds() const GSL_NOEXCEPT { return m_extents; }
+    constexpr const_iterator begin() const GSL_NOEXCEPT
+    {
+        return const_iterator{*this, index_type{}};
+    }
 
-    constexpr const_iterator end() const noexcept { return const_iterator{*this, index_bounds()}; }
+    constexpr const_iterator end() const GSL_NOEXCEPT
+    {
+        return const_iterator{*this, index_bounds()};
+    }
 
 private:
     index_type m_extents;
@@ -807,7 +800,7 @@
 struct is_bounds<static_bounds<Ranges...>> : std::integral_constant<bool, true>
 {
 };
-template <size_t Rank>
+template <std::size_t Rank>
 struct is_bounds<strided_bounds<Rank>> : std::integral_constant<bool, true>
 {
 };
@@ -819,7 +812,7 @@
     using Base = std::iterator<std::random_access_iterator_tag, IndexType>;
 
 public:
-    static const size_t rank = IndexType::rank;
+    static const std::size_t rank = IndexType::rank;
     using typename Base::reference;
     using typename Base::pointer;
     using typename Base::difference_type;
@@ -827,20 +820,20 @@
     using index_type = value_type;
     using index_size_type = typename IndexType::value_type;
     template <typename Bounds>
-    explicit bounds_iterator(const Bounds& bnd, value_type curr) noexcept
+    explicit bounds_iterator(const Bounds& bnd, value_type curr) GSL_NOEXCEPT
         : boundary_(bnd.index_bounds()),
           curr_(std::move(curr))
     {
         static_assert(is_bounds<Bounds>::value, "Bounds type must be provided");
     }
 
-    constexpr reference operator*() const noexcept { return curr_; }
+    constexpr reference operator*() const GSL_NOEXCEPT { return curr_; }
 
-    constexpr pointer operator->() const noexcept { return &curr_; }
+    constexpr pointer operator->() const GSL_NOEXCEPT { return &curr_; }
 
-    constexpr bounds_iterator& operator++() noexcept
+    constexpr bounds_iterator& operator++() GSL_NOEXCEPT
     {
-        for (size_t i = rank; i-- > 0;) {
+        for (std::size_t i = rank; i-- > 0;) {
             if (curr_[i] < boundary_[i] - 1) {
                 curr_[i]++;
                 return *this;
@@ -852,23 +845,23 @@
         return *this;
     }
 
-    constexpr bounds_iterator operator++(int) noexcept
+    constexpr bounds_iterator operator++(int) GSL_NOEXCEPT
     {
         auto ret = *this;
         ++(*this);
         return ret;
     }
 
-    constexpr bounds_iterator& operator--() noexcept
+    constexpr bounds_iterator& operator--() GSL_NOEXCEPT
     {
         if (!less(curr_, boundary_)) {
             // if at the past-the-end, set to last element
-            for (size_t i = 0; i < rank; ++i) {
+            for (std::size_t i = 0; i < rank; ++i) {
                 curr_[i] = boundary_[i] - 1;
             }
             return *this;
         }
-        for (size_t i = rank; i-- > 0;) {
+        for (std::size_t i = rank; i-- > 0;) {
             if (curr_[i] >= 1) {
                 curr_[i]--;
                 return *this;
@@ -881,28 +874,28 @@
         return *this;
     }
 
-    constexpr bounds_iterator operator--(int) noexcept
+    constexpr bounds_iterator operator--(int) GSL_NOEXCEPT
     {
         auto ret = *this;
         --(*this);
         return ret;
     }
 
-    constexpr bounds_iterator operator+(difference_type n) const noexcept
+    constexpr bounds_iterator operator+(difference_type n) const GSL_NOEXCEPT
     {
         bounds_iterator ret{*this};
         return ret += n;
     }
 
-    constexpr bounds_iterator& operator+=(difference_type n) noexcept
+    constexpr bounds_iterator& operator+=(difference_type n) GSL_NOEXCEPT
     {
         auto linear_idx = linearize(curr_) + n;
         std::remove_const_t<value_type> stride = 0;
         stride[rank - 1] = 1;
-        for (size_t i = rank - 1; i-- > 0;) {
+        for (std::size_t i = rank - 1; i-- > 0;) {
             stride[i] = stride[i + 1] * boundary_[i + 1];
         }
-        for (size_t i = 0; i < rank; ++i) {
+        for (std::size_t i = 0; i < rank; ++i) {
             curr_[i] = linear_idx / stride[i];
             linear_idx = linear_idx % stride[i];
         }
@@ -911,55 +904,64 @@
         return *this;
     }
 
-    constexpr bounds_iterator operator-(difference_type n) const noexcept
+    constexpr bounds_iterator operator-(difference_type n) const GSL_NOEXCEPT
     {
         bounds_iterator ret{*this};
         return ret -= n;
     }
 
-    constexpr bounds_iterator& operator-=(difference_type n) noexcept { return *this += -n; }
+    constexpr bounds_iterator& operator-=(difference_type n) GSL_NOEXCEPT { return *this += -n; }
 
-    constexpr difference_type operator-(const bounds_iterator& rhs) const noexcept
+    constexpr difference_type operator-(const bounds_iterator& rhs) const GSL_NOEXCEPT
     {
         return linearize(curr_) - linearize(rhs.curr_);
     }
 
-    constexpr value_type operator[](difference_type n) const noexcept { return *(*this + n); }
+    constexpr value_type operator[](difference_type n) const GSL_NOEXCEPT { return *(*this + n); }
 
-    constexpr bool operator==(const bounds_iterator& rhs) const noexcept
+    constexpr bool operator==(const bounds_iterator& rhs) const GSL_NOEXCEPT
     {
         return curr_ == rhs.curr_;
     }
 
-    constexpr bool operator!=(const bounds_iterator& rhs) const noexcept { return !(*this == rhs); }
+    constexpr bool operator!=(const bounds_iterator& rhs) const GSL_NOEXCEPT
+    {
+        return !(*this == rhs);
+    }
 
-    constexpr bool operator<(const bounds_iterator& rhs) const noexcept
+    constexpr bool operator<(const bounds_iterator& rhs) const GSL_NOEXCEPT
     {
         return less(curr_, rhs.curr_);
     }
 
-    constexpr bool operator<=(const bounds_iterator& rhs) const noexcept { return !(rhs < *this); }
+    constexpr bool operator<=(const bounds_iterator& rhs) const GSL_NOEXCEPT
+    {
+        return !(rhs < *this);
+    }
 
-    constexpr bool operator>(const bounds_iterator& rhs) const noexcept { return rhs < *this; }
+    constexpr bool operator>(const bounds_iterator& rhs) const GSL_NOEXCEPT { return rhs < *this; }
 
-    constexpr bool operator>=(const bounds_iterator& rhs) const noexcept { return !(rhs > *this); }
+    constexpr bool operator>=(const bounds_iterator& rhs) const GSL_NOEXCEPT
+    {
+        return !(rhs > *this);
+    }
 
-    void swap(bounds_iterator& rhs) noexcept
+    void swap(bounds_iterator& rhs) GSL_NOEXCEPT
     {
         std::swap(boundary_, rhs.boundary_);
         std::swap(curr_, rhs.curr_);
     }
 
 private:
-    constexpr bool less(index_type& one, index_type& other) const noexcept
+    constexpr bool less(index_type& one, index_type& other) const GSL_NOEXCEPT
     {
-        for (size_t i = 0; i < rank; ++i) {
+        for (std::size_t i = 0; i < rank; ++i) {
             if (one[i] < other[i]) return true;
         }
         return false;
     }
 
-    constexpr index_size_type linearize(const value_type& idx) const noexcept
+    constexpr index_size_type linearize(const value_type& idx) const GSL_NOEXCEPT
     {
         // TODO: Smarter impl.
         // Check if past-the-end
@@ -967,14 +969,14 @@
         index_size_type res = 0;
         if (!less(idx, boundary_)) {
             res = 1;
-            for (size_t i = rank; i-- > 0;) {
+            for (std::size_t i = rank; i-- > 0;) {
                 res += (idx[i] - 1) * multiplier;
                 multiplier *= boundary_[i];
             }
         }
         else
         {
-            for (size_t i = rank; i-- > 0;) {
+            for (std::size_t i = rank; i-- > 0;) {
                 res += idx[i] * multiplier;
                 multiplier *= boundary_[i];
             }
@@ -988,7 +990,7 @@
 
 template <typename IndexType>
 bounds_iterator<IndexType> operator+(typename bounds_iterator<IndexType>::difference_type n,
-                                     const bounds_iterator<IndexType>& rhs) noexcept
+                                     const bounds_iterator<IndexType>& rhs) GSL_NOEXCEPT
 {
     return rhs + n;
 }
@@ -996,26 +998,26 @@
 namespace details
 {
     template <typename Bounds>
-    constexpr std::enable_if_t<
+    inline constexpr std::enable_if_t<
         std::is_same<typename Bounds::mapping_type, generalized_mapping_tag>::value,
         typename Bounds::index_type>
-    make_stride(const Bounds& bnd) noexcept
+    make_stride(const Bounds& bnd) GSL_NOEXCEPT
     {
         return bnd.strides();
     }
 
     // Make a stride vector from bounds, assuming contiguous memory.
     template <typename Bounds>
-    constexpr std::enable_if_t<
+    inline constexpr std::enable_if_t<
         std::is_same<typename Bounds::mapping_type, contiguous_mapping_tag>::value,
         typename Bounds::index_type>
-    make_stride(const Bounds& bnd) noexcept
+    make_stride(const Bounds& bnd) GSL_NOEXCEPT
     {
         auto extents = bnd.index_bounds();
         typename Bounds::size_type stride[Bounds::rank] = {};
 
         stride[Bounds::rank - 1] = 1;
-        for (size_t i = 1; i < Bounds::rank; ++i) {
+        for (std::size_t i = 1; i < Bounds::rank; ++i) {
             stride[Bounds::rank - i - 1] = stride[Bounds::rank - i] * extents[Bounds::rank - i];
         }
         return {stride};
@@ -1055,14 +1057,14 @@
     dim_t(std::ptrdiff_t size) : dvalue(size) {}
 };
 
-template <std::ptrdiff_t N>
-constexpr std::enable_if_t<(N >= 0), dim_t<N>> dim() noexcept
+template <std::ptrdiff_t N, class = std::enable_if_t<(N >= 0)>>
+inline constexpr dim_t<N> dim() GSL_NOEXCEPT
 {
     return dim_t<N>();
 }
 
-template <std::ptrdiff_t N = dynamic_range>
-constexpr std::enable_if_t<N == dynamic_range, dim_t<N>> dim(std::ptrdiff_t n) noexcept
+template <std::ptrdiff_t N = dynamic_range, class = std::enable_if_t<N == dynamic_range>>
+inline constexpr dim_t<N> dim(std::ptrdiff_t n) GSL_NOEXCEPT
 {
     return dim_t<>(n);
 }
@@ -1071,7 +1073,7 @@
           std::ptrdiff_t... RestDimensions>
 class multi_span;
 
-template <typename ValueType, size_t Rank>
+template <typename ValueType, std::size_t Rank>
 class strided_span;
 
 namespace details
@@ -1080,7 +1082,7 @@
     struct SpanTypeTraits
     {
         using value_type = T;
-        using size_type = size_t;
+        using size_type = std::size_t;
     };
 
     template <typename Traits>
@@ -1136,7 +1138,7 @@
     template <typename T, typename Arg, typename... Args>
     std::enable_if_t<
         !std::is_same<Arg, dim_t<dynamic_range>>::value && !std::is_same<Arg, Sep>::value, T>
-        static_as_multi_span_helper(Arg, Args... args)
+    static_as_multi_span_helper(Arg, Args... args)
     {
         return static_as_multi_span_helper<T>(args...);
     }
@@ -1184,7 +1186,7 @@
 
 public:
     using bounds_type = static_bounds<FirstDimension, RestDimensions...>;
-    static const size_t Rank = bounds_type::rank;
+    static const std::size_t Rank = bounds_type::rank;
     using size_type = typename bounds_type::size_type;
     using index_type = typename bounds_type::index_type;
     using value_type = ValueType;
@@ -1208,7 +1210,7 @@
 
 public:
     // default constructor - same as constructing from nullptr_t
-    constexpr multi_span() noexcept : multi_span(nullptr, bounds_type{})
+    constexpr multi_span() GSL_NOEXCEPT : multi_span(nullptr, bounds_type{})
     {
         static_assert(bounds_type::dynamic_rank != 0 ||
                           (bounds_type::dynamic_rank == 0 && bounds_type::static_size == 0),
@@ -1217,7 +1219,7 @@
     }
 
     // construct from nullptr - get an empty multi_span
-    constexpr multi_span(std::nullptr_t) noexcept : multi_span(nullptr, bounds_type{})
+    constexpr multi_span(std::nullptr_t) GSL_NOEXCEPT : multi_span(nullptr, bounds_type{})
     {
         static_assert(bounds_type::dynamic_rank != 0 ||
                           (bounds_type::dynamic_rank == 0 && bounds_type::static_size == 0),
@@ -1227,7 +1229,8 @@
 
     // construct from nullptr with size of 0 (helps with template function calls)
     template <class IntType, typename = std::enable_if_t<std::is_integral<IntType>::value>>
-    constexpr multi_span(std::nullptr_t, IntType size) noexcept : multi_span(nullptr, bounds_type{})
+    constexpr multi_span(std::nullptr_t, IntType size) GSL_NOEXCEPT
+        : multi_span(nullptr, bounds_type{})
     {
         static_assert(bounds_type::dynamic_rank != 0 ||
                           (bounds_type::dynamic_rank == 0 && bounds_type::static_size == 0),
@@ -1237,7 +1240,7 @@
     }
 
     // construct from a single element
-    constexpr multi_span(reference data) noexcept : multi_span(&data, bounds_type{1})
+    constexpr multi_span(reference data) GSL_NOEXCEPT : multi_span(&data, bounds_type{1})
     {
         static_assert(bounds_type::dynamic_rank > 0 || bounds_type::static_size == 0 ||
                           bounds_type::static_size == 1,
@@ -1249,13 +1252,14 @@
     constexpr multi_span(value_type&&) = delete;
 
     // construct from pointer + length
-    constexpr multi_span(pointer ptr, size_type size) noexcept : multi_span(ptr, bounds_type{size})
+    constexpr multi_span(pointer ptr, size_type size) GSL_NOEXCEPT
+        : multi_span(ptr, bounds_type{size})
     {
     }
 
     // construct from pointer + length - multidimensional
-    constexpr multi_span(pointer data, bounds_type bounds) noexcept : data_(data),
-                                                                      bounds_(std::move(bounds))
+    constexpr multi_span(pointer data, bounds_type bounds) GSL_NOEXCEPT : data_(data),
+                                                                          bounds_(std::move(bounds))
     {
         Expects((bounds_.size() > 0 && data != nullptr) || bounds_.size() == 0);
     }
@@ -1272,7 +1276,7 @@
     }
 
     // construct from n-dimensions static array
-    template <typename T, size_t N, typename Helper = details::SpanArrayTraits<T, N>>
+    template <typename T, std::size_t N, typename Helper = details::SpanArrayTraits<T, N>>
     constexpr multi_span(T (&arr)[N])
         : multi_span(reinterpret_cast<pointer>(arr), bounds_type{typename Helper::bounds_type{}})
     {
@@ -1293,7 +1297,7 @@
     }
 
     // construct from std::array
-    template <typename T, size_t N>
+    template <typename T, std::size_t N>
     constexpr multi_span(std::array<T, N>& arr)
         : multi_span(arr.data(), bounds_type{static_bounds<N>{}})
     {
@@ -1305,18 +1309,18 @@
     }
 
     // construct from const std::array
-    template <typename T, size_t N>
-    constexpr multi_span(const std::array<std::remove_const_t<value_type>, N>& arr)
-        : multi_span(arr.data(), static_bounds<N>())
+    template <typename T, std::size_t N>
+    constexpr multi_span(const std::array<T, N>& arr)
+        : multi_span(arr.data(), bounds_type{static_bounds<N>{}})
     {
-        static_assert(std::is_convertible<T(*)[], std::remove_const_t<value_type>>::value,
+        static_assert(std::is_convertible<T(*)[], typename std::remove_const_t<value_type>(*)[]>::value,
                       "Cannot convert from source type to target multi_span type.");
         static_assert(std::is_convertible<static_bounds<N>, bounds_type>::value,
                       "You cannot construct a multi_span from a std::array of smaller size.");
     }
 
     // prevent constructing from temporary std::array
-    template <typename T, size_t N>
+    template <typename T, std::size_t N>
     constexpr multi_span(std::array<T, N>&& arr) = delete;
 
     // construct from containers
@@ -1350,27 +1354,23 @@
               typename OtherBounds = static_bounds<OtherDimensions...>,
               typename = std::enable_if_t<std::is_convertible<OtherValueType, ValueType>::value &&
                                           std::is_convertible<OtherBounds, bounds_type>::value>>
-    constexpr multi_span(multi_span<OtherValueType, OtherDimensions...> other) noexcept
+    constexpr multi_span(multi_span<OtherValueType, OtherDimensions...> other) GSL_NOEXCEPT
         : data_(other.data_),
           bounds_(other.bounds_)
     {
     }
 
-// trivial copy and move
-#ifndef GSL_MSVC_NO_SUPPORT_FOR_MOVE_CTOR_DEFAULT
-    constexpr multi_span(multi_span&&) = default;
-#endif
+    // trivial copy and move
     constexpr multi_span(const multi_span&) = default;
+    constexpr multi_span(multi_span&&) = default;
 
-// trivial assignment
-#ifndef GSL_MSVC_NO_SUPPORT_FOR_MOVE_CTOR_DEFAULT
-    constexpr multi_span& operator=(multi_span&&) = default;
-#endif
+    // trivial assignment
     constexpr multi_span& operator=(const multi_span&) = default;
+    constexpr multi_span& operator=(multi_span&&) = default;
 
     // first() - extract the first Count elements into a new multi_span
     template <std::ptrdiff_t Count>
-    constexpr multi_span<ValueType, Count> first() const noexcept
+    constexpr multi_span<ValueType, Count> first() const GSL_NOEXCEPT
     {
         static_assert(Count >= 0, "Count must be >= 0.");
         static_assert(bounds_type::static_size == dynamic_range ||
@@ -1382,7 +1382,7 @@
     }
 
     // first() - extract the first count elements into a new multi_span
-    constexpr multi_span<ValueType, dynamic_range> first(size_type count) const noexcept
+    constexpr multi_span<ValueType, dynamic_range> first(size_type count) const GSL_NOEXCEPT
     {
         Expects(count >= 0 && count <= this->size());
         return {this->data(), count};
@@ -1390,7 +1390,7 @@
 
     // last() - extract the last Count elements into a new multi_span
     template <std::ptrdiff_t Count>
-    constexpr multi_span<ValueType, Count> last() const noexcept
+    constexpr multi_span<ValueType, Count> last() const GSL_NOEXCEPT
     {
         static_assert(Count >= 0, "Count must be >= 0.");
         static_assert(bounds_type::static_size == dynamic_range ||
@@ -1402,7 +1402,7 @@
     }
 
     // last() - extract the last count elements into a new multi_span
-    constexpr multi_span<ValueType, dynamic_range> last(size_type count) const noexcept
+    constexpr multi_span<ValueType, dynamic_range> last(size_type count) const GSL_NOEXCEPT
     {
         Expects(count >= 0 && count <= this->size());
         return {this->data() + this->size() - count, count};
@@ -1410,7 +1410,7 @@
 
     // subspan() - create a subview of Count elements starting at Offset
     template <std::ptrdiff_t Offset, std::ptrdiff_t Count>
-    constexpr multi_span<ValueType, Count> subspan() const noexcept
+    constexpr multi_span<ValueType, Count> subspan() const GSL_NOEXCEPT
     {
         static_assert(Count >= 0, "Count must be >= 0.");
         static_assert(Offset >= 0, "Offset must be >= 0.");
@@ -1426,9 +1426,8 @@
 
     // subspan() - create a subview of count elements starting at offset
     // supplying dynamic_range for count will consume all available elements from offset
-    constexpr multi_span<ValueType, dynamic_range> subspan(size_type offset,
-                                                           size_type count = dynamic_range) const
-        noexcept
+    constexpr multi_span<ValueType, dynamic_range>
+    subspan(size_type offset, size_type count = dynamic_range) const GSL_NOEXCEPT
     {
         Expects((offset >= 0 && offset <= this->size()) &&
                 (count == dynamic_range || (count <= this->size() - offset)));
@@ -1436,8 +1435,8 @@
     }
 
     // section - creates a non-contiguous, strided multi_span from a contiguous one
-    constexpr strided_span<ValueType, Rank> section(index_type origin, index_type extents) const
-        noexcept
+    constexpr strided_span<ValueType, Rank> section(index_type origin,
+                                                    index_type extents) const GSL_NOEXCEPT
     {
         size_type size = this->bounds().total_size() - this->bounds().linearize(origin);
         return {&this->operator[](origin), size,
@@ -1445,23 +1444,26 @@
     }
 
     // length of the multi_span in elements
-    constexpr size_type size() const noexcept { return bounds_.size(); }
+    constexpr size_type size() const GSL_NOEXCEPT { return bounds_.size(); }
 
     // length of the multi_span in elements
-    constexpr size_type length() const noexcept { return this->size(); }
+    constexpr size_type length() const GSL_NOEXCEPT { return this->size(); }
 
     // length of the multi_span in bytes
-    constexpr size_type size_bytes() const noexcept { return sizeof(value_type) * this->size(); }
+    constexpr size_type size_bytes() const GSL_NOEXCEPT
+    {
+        return narrow_cast<size_type>(sizeof(value_type)) * this->size();
+    }
 
     // length of the multi_span in bytes
-    constexpr size_type length_bytes() const noexcept { return this->size_bytes(); }
+    constexpr size_type length_bytes() const GSL_NOEXCEPT { return this->size_bytes(); }
 
-    constexpr bool empty() const noexcept { return this->size() == 0; }
+    constexpr bool empty() const GSL_NOEXCEPT { return this->size() == 0; }
 
     static constexpr std::size_t rank() { return Rank; }
 
-    template <size_t Dim = 0>
-    constexpr size_type extent() const noexcept
+    template <std::size_t Dim = 0>
+    constexpr size_type extent() const GSL_NOEXCEPT
     {
         static_assert(Dim < Rank,
                       "Dimension should be less than rank (dimension count starts from 0).");
@@ -1469,14 +1471,14 @@
     }
 
     template <typename IntType>
-    constexpr size_type extent(IntType dim) const noexcept
+    constexpr size_type extent(IntType dim) const GSL_NOEXCEPT
     {
         return bounds_.extent(dim);
     }
 
-    constexpr bounds_type bounds() const noexcept { return bounds_; }
+    constexpr bounds_type bounds() const GSL_NOEXCEPT { return bounds_; }
 
-    constexpr pointer data() const noexcept { return data_; }
+    constexpr pointer data() const GSL_NOEXCEPT { return data_; }
 
     template <typename FirstIndex>
     constexpr reference operator()(FirstIndex index)
@@ -1488,17 +1490,17 @@
     constexpr reference operator()(FirstIndex index, OtherIndices... indices)
     {
         index_type idx = {narrow_cast<std::ptrdiff_t>(index),
-                          narrow_cast<std::ptrdiff_t>(indices...)};
+                          narrow_cast<std::ptrdiff_t>(indices)...};
         return this->operator[](idx);
     }
 
-    constexpr reference operator[](const index_type& idx) const noexcept
+    constexpr reference operator[](const index_type& idx) const GSL_NOEXCEPT
     {
         return data_[bounds_.linearize(idx)];
     }
 
     template <bool Enabled = (Rank > 1), typename Ret = std::enable_if_t<Enabled, sliced_type>>
-    constexpr Ret operator[](size_type idx) const noexcept
+    constexpr Ret operator[](size_type idx) const GSL_NOEXCEPT
     {
         Expects(idx >= 0 && idx < bounds_.size()); // index is out of bounds of the array
         const size_type ridx = idx * bounds_.stride();
@@ -1508,30 +1510,30 @@
         return Ret{data_ + ridx, bounds_.slice()};
     }
 
-    constexpr iterator begin() const noexcept { return iterator{this, true}; }
+    constexpr iterator begin() const GSL_NOEXCEPT { return iterator{this, true}; }
 
-    constexpr iterator end() const noexcept { return iterator{this, false}; }
+    constexpr iterator end() const GSL_NOEXCEPT { return iterator{this, false}; }
 
-    constexpr const_iterator cbegin() const noexcept
+    constexpr const_iterator cbegin() const GSL_NOEXCEPT
     {
         return const_iterator{reinterpret_cast<const const_span*>(this), true};
     }
 
-    constexpr const_iterator cend() const noexcept
+    constexpr const_iterator cend() const GSL_NOEXCEPT
     {
         return const_iterator{reinterpret_cast<const const_span*>(this), false};
     }
 
-    constexpr reverse_iterator rbegin() const noexcept { return reverse_iterator{end()}; }
+    constexpr reverse_iterator rbegin() const GSL_NOEXCEPT { return reverse_iterator{end()}; }
 
-    constexpr reverse_iterator rend() const noexcept { return reverse_iterator{begin()}; }
+    constexpr reverse_iterator rend() const GSL_NOEXCEPT { return reverse_iterator{begin()}; }
 
-    constexpr const_reverse_iterator crbegin() const noexcept
+    constexpr const_reverse_iterator crbegin() const GSL_NOEXCEPT
     {
         return const_reverse_iterator{cend()};
     }
 
-    constexpr const_reverse_iterator crend() const noexcept
+    constexpr const_reverse_iterator crend() const GSL_NOEXCEPT
     {
         return const_reverse_iterator{cbegin()};
     }
@@ -1539,8 +1541,8 @@
     template <typename OtherValueType, std::ptrdiff_t... OtherDimensions,
               typename Dummy = std::enable_if_t<std::is_same<
                   std::remove_cv_t<value_type>, std::remove_cv_t<OtherValueType>>::value>>
-    constexpr bool operator==(const multi_span<OtherValueType, OtherDimensions...>& other) const
-        noexcept
+    constexpr bool
+    operator==(const multi_span<OtherValueType, OtherDimensions...>& other) const GSL_NOEXCEPT
     {
         return bounds_.size() == other.bounds_.size() &&
                (data_ == other.data_ || std::equal(this->begin(), this->end(), other.begin()));
@@ -1549,8 +1551,8 @@
     template <typename OtherValueType, std::ptrdiff_t... OtherDimensions,
               typename Dummy = std::enable_if_t<std::is_same<
                   std::remove_cv_t<value_type>, std::remove_cv_t<OtherValueType>>::value>>
-    constexpr bool operator!=(const multi_span<OtherValueType, OtherDimensions...>& other) const
-        noexcept
+    constexpr bool
+    operator!=(const multi_span<OtherValueType, OtherDimensions...>& other) const GSL_NOEXCEPT
     {
         return !(*this == other);
     }
@@ -1558,8 +1560,8 @@
     template <typename OtherValueType, std::ptrdiff_t... OtherDimensions,
               typename Dummy = std::enable_if_t<std::is_same<
                   std::remove_cv_t<value_type>, std::remove_cv_t<OtherValueType>>::value>>
-    constexpr bool operator<(const multi_span<OtherValueType, OtherDimensions...>& other) const
-        noexcept
+    constexpr bool
+    operator<(const multi_span<OtherValueType, OtherDimensions...>& other) const GSL_NOEXCEPT
     {
         return std::lexicographical_compare(this->begin(), this->end(), other.begin(), other.end());
     }
@@ -1567,8 +1569,8 @@
     template <typename OtherValueType, std::ptrdiff_t... OtherDimensions,
               typename Dummy = std::enable_if_t<std::is_same<
                   std::remove_cv_t<value_type>, std::remove_cv_t<OtherValueType>>::value>>
-    constexpr bool operator<=(const multi_span<OtherValueType, OtherDimensions...>& other) const
-        noexcept
+    constexpr bool
+    operator<=(const multi_span<OtherValueType, OtherDimensions...>& other) const GSL_NOEXCEPT
     {
         return !(other < *this);
     }
@@ -1576,8 +1578,8 @@
     template <typename OtherValueType, std::ptrdiff_t... OtherDimensions,
               typename Dummy = std::enable_if_t<std::is_same<
                   std::remove_cv_t<value_type>, std::remove_cv_t<OtherValueType>>::value>>
-    constexpr bool operator>(const multi_span<OtherValueType, OtherDimensions...>& other) const
-        noexcept
+    constexpr bool
+    operator>(const multi_span<OtherValueType, OtherDimensions...>& other) const GSL_NOEXCEPT
     {
         return (other < *this);
     }
@@ -1585,8 +1587,8 @@
     template <typename OtherValueType, std::ptrdiff_t... OtherDimensions,
               typename Dummy = std::enable_if_t<std::is_same<
                   std::remove_cv_t<value_type>, std::remove_cv_t<OtherValueType>>::value>>
-    constexpr bool operator>=(const multi_span<OtherValueType, OtherDimensions...>& other) const
-        noexcept
+    constexpr bool
+    operator>=(const multi_span<OtherValueType, OtherDimensions...>& other) const GSL_NOEXCEPT
     {
         return !(*this < other);
     }
@@ -1598,9 +1600,9 @@
 
 // reshape a multi_span into a different dimensionality
 // DimCount and Enabled here are workarounds for a bug in MSVC 2015
-template <typename SpanType, typename... Dimensions2, size_t DimCount = sizeof...(Dimensions2),
+template <typename SpanType, typename... Dimensions2, std::size_t DimCount = sizeof...(Dimensions2),
           bool Enabled = (DimCount > 0), typename = std::enable_if_t<Enabled>>
-constexpr auto as_multi_span(SpanType s, Dimensions2... dims)
+inline constexpr auto as_multi_span(SpanType s, Dimensions2... dims)
     -> multi_span<typename SpanType::value_type, Dimensions2::value...>
 {
     static_assert(details::is_multi_span<SpanType>::value,
@@ -1614,7 +1616,7 @@
 
 // convert a multi_span<T> to a multi_span<const byte>
 template <typename U, std::ptrdiff_t... Dimensions>
-multi_span<const byte, dynamic_range> as_bytes(multi_span<U, Dimensions...> s) noexcept
+multi_span<const byte, dynamic_range> as_bytes(multi_span<U, Dimensions...> s) GSL_NOEXCEPT
 {
     static_assert(std::is_trivial<std::decay_t<U>>::value,
                   "The value_type of multi_span must be a trivial type.");
@@ -1626,7 +1628,7 @@
 // on all implementations. It should be considered an experimental extension
 // to the standard GSL interface.
 template <typename U, std::ptrdiff_t... Dimensions>
-multi_span<byte> as_writeable_bytes(multi_span<U, Dimensions...> s) noexcept
+multi_span<byte> as_writeable_bytes(multi_span<U, Dimensions...> s) GSL_NOEXCEPT
 {
     static_assert(std::is_trivial<std::decay_t<U>>::value,
                   "The value_type of multi_span must be a trivial type.");
@@ -1638,10 +1640,11 @@
 // on all implementations. It should be considered an experimental extension
 // to the standard GSL interface.
 template <typename U, std::ptrdiff_t... Dimensions>
-constexpr auto as_multi_span(multi_span<const byte, Dimensions...> s) noexcept -> multi_span<
+inline constexpr auto
+as_multi_span(multi_span<const byte, Dimensions...> s) GSL_NOEXCEPT -> multi_span<
     const U, static_cast<std::ptrdiff_t>(
                  multi_span<const byte, Dimensions...>::bounds_type::static_size != dynamic_range
-                     ? (static_cast<size_t>(
+                     ? (static_cast<std::size_t>(
                             multi_span<const byte, Dimensions...>::bounds_type::static_size) /
                         sizeof(U))
                      : dynamic_range)>
@@ -1653,7 +1656,8 @@
              ConstByteSpan::bounds_type::static_size % narrow_cast<std::ptrdiff_t>(sizeof(U)) == 0),
         "Target type must be a trivial type and its size must match the byte array size");
 
-    Expects((s.size_bytes() % sizeof(U)) == 0 && (s.size_bytes() / sizeof(U)) < PTRDIFF_MAX);
+    Expects((s.size_bytes() % narrow_cast<std::ptrdiff_t>(sizeof(U))) == 0 &&
+            (s.size_bytes() / narrow_cast<std::ptrdiff_t>(sizeof(U))) < PTRDIFF_MAX);
     return {reinterpret_cast<const U*>(s.data()),
             s.size_bytes() / narrow_cast<std::ptrdiff_t>(sizeof(U))};
 }
@@ -1663,7 +1667,7 @@
 // on all implementations. It should be considered an experimental extension
 // to the standard GSL interface.
 template <typename U, std::ptrdiff_t... Dimensions>
-constexpr auto as_multi_span(multi_span<byte, Dimensions...> s) noexcept
+inline constexpr auto as_multi_span(multi_span<byte, Dimensions...> s) GSL_NOEXCEPT
     -> multi_span<U, narrow_cast<std::ptrdiff_t>(
                          multi_span<byte, Dimensions...>::bounds_type::static_size != dynamic_range
                              ? static_cast<std::size_t>(
@@ -1684,7 +1688,7 @@
 }
 
 template <typename T, std::ptrdiff_t... Dimensions>
-constexpr auto as_multi_span(T* const& ptr, dim_t<Dimensions>... args)
+inline constexpr auto as_multi_span(T* const& ptr, dim_t<Dimensions>... args)
     -> multi_span<std::remove_all_extents_t<T>, Dimensions...>
 {
     return {reinterpret_cast<std::remove_all_extents_t<T>*>(ptr),
@@ -1693,41 +1697,41 @@
 }
 
 template <typename T>
-constexpr auto as_multi_span(T* arr, std::ptrdiff_t len) ->
+inline constexpr auto as_multi_span(T* arr, std::ptrdiff_t len) ->
     typename details::SpanArrayTraits<T, dynamic_range>::type
 {
     return {reinterpret_cast<std::remove_all_extents_t<T>*>(arr), len};
 }
 
-template <typename T, size_t N>
-constexpr auto as_multi_span(T (&arr)[N]) -> typename details::SpanArrayTraits<T, N>::type
+template <typename T, std::size_t N>
+inline constexpr auto as_multi_span(T (&arr)[N]) -> typename details::SpanArrayTraits<T, N>::type
 {
     return {arr};
 }
 
-template <typename T, size_t N>
-constexpr multi_span<const T, N> as_multi_span(const std::array<T, N>& arr)
+template <typename T, std::size_t N>
+inline constexpr multi_span<const T, N> as_multi_span(const std::array<T, N>& arr)
 {
     return {arr};
 }
 
-template <typename T, size_t N>
-constexpr multi_span<const T, N> as_multi_span(const std::array<T, N>&&) = delete;
+template <typename T, std::size_t N>
+inline constexpr multi_span<const T, N> as_multi_span(const std::array<T, N>&&) = delete;
 
-template <typename T, size_t N>
-constexpr multi_span<T, N> as_multi_span(std::array<T, N>& arr)
+template <typename T, std::size_t N>
+inline constexpr multi_span<T, N> as_multi_span(std::array<T, N>& arr)
 {
     return {arr};
 }
 
 template <typename T>
-constexpr multi_span<T, dynamic_range> as_multi_span(T* begin, T* end)
+inline constexpr multi_span<T, dynamic_range> as_multi_span(T* begin, T* end)
 {
     return {begin, end};
 }
 
 template <typename Cont>
-constexpr auto as_multi_span(Cont& arr) -> std::enable_if_t<
+inline constexpr auto as_multi_span(Cont& arr) -> std::enable_if_t<
     !details::is_multi_span<std::decay_t<Cont>>::value,
     multi_span<std::remove_reference_t<decltype(arr.size(), *arr.data())>, dynamic_range>>
 {
@@ -1736,13 +1740,13 @@
 }
 
 template <typename Cont>
-constexpr auto as_multi_span(Cont&& arr) -> std::enable_if_t<
+inline constexpr auto as_multi_span(Cont&& arr) -> std::enable_if_t<
     !details::is_multi_span<std::decay_t<Cont>>::value,
     multi_span<std::remove_reference_t<decltype(arr.size(), *arr.data())>, dynamic_range>> = delete;
 
 // from basic_string which doesn't have nonconst .data() member like other contiguous containers
 template <typename CharT, typename Traits, typename Allocator>
-constexpr auto as_multi_span(std::basic_string<CharT, Traits, Allocator>& str)
+inline constexpr auto as_multi_span(std::basic_string<CharT, Traits, Allocator>& str)
     -> multi_span<CharT, dynamic_range>
 {
     Expects(str.size() < PTRDIFF_MAX);
@@ -1751,7 +1755,7 @@
 
 // strided_span is an extension that is not strictly part of the GSL at this time.
 // It is kept here while the multidimensional interface is still being defined.
-template <typename ValueType, size_t Rank>
+template <typename ValueType, std::size_t Rank>
 class strided_span
 {
 public:
@@ -1776,7 +1780,7 @@
 
     friend iterator;
     friend const_iterator;
-    template <typename OtherValueType, size_t OtherRank>
+    template <typename OtherValueType, std::size_t OtherRank>
     friend class strided_span;
 
 public:
@@ -1829,8 +1833,9 @@
 
         size_type size = this->bounds().total_size() / d;
         return {const_cast<OtherValueType*>(reinterpret_cast<const OtherValueType*>(this->data())),
-                size, bounds_type{resize_extent(this->bounds().index_bounds(), d),
-                                  resize_stride(this->bounds().strides(), d)}};
+                size,
+                bounds_type{resize_extent(this->bounds().index_bounds(), d),
+                            resize_stride(this->bounds().strides(), d)}};
     }
 
     constexpr strided_span section(index_type origin, index_type extents) const
@@ -1856,21 +1861,21 @@
         return {data_ + ridx, bounds_.slice().total_size(), bounds_.slice()};
     }
 
-    constexpr bounds_type bounds() const noexcept { return bounds_; }
+    constexpr bounds_type bounds() const GSL_NOEXCEPT { return bounds_; }
 
-    template <size_t Dim = 0>
-    constexpr size_type extent() const noexcept
+    template <std::size_t Dim = 0>
+    constexpr size_type extent() const GSL_NOEXCEPT
     {
         static_assert(Dim < Rank,
                       "dimension should be less than Rank (dimension count starts from 0)");
         return bounds_.template extent<Dim>();
     }
 
-    constexpr size_type size() const noexcept { return bounds_.size(); }
+    constexpr size_type size() const GSL_NOEXCEPT { return bounds_.size(); }
 
-    constexpr pointer data() const noexcept { return data_; }
+    constexpr pointer data() const GSL_NOEXCEPT { return data_; }
 
-    constexpr explicit operator bool() const noexcept { return data_ != nullptr; }
+    constexpr explicit operator bool() const GSL_NOEXCEPT { return data_ != nullptr; }
 
     constexpr iterator begin() const { return iterator{this, true}; }
 
@@ -1897,7 +1902,8 @@
     template <typename OtherValueType, std::ptrdiff_t OtherRank,
               typename Dummy = std::enable_if_t<std::is_same<
                   std::remove_cv_t<value_type>, std::remove_cv_t<OtherValueType>>::value>>
-    constexpr bool operator==(const strided_span<OtherValueType, OtherRank>& other) const noexcept
+    constexpr bool
+    operator==(const strided_span<OtherValueType, OtherRank>& other) const GSL_NOEXCEPT
     {
         return bounds_.size() == other.bounds_.size() &&
                (data_ == other.data_ || std::equal(this->begin(), this->end(), other.begin()));
@@ -1906,7 +1912,8 @@
     template <typename OtherValueType, std::ptrdiff_t OtherRank,
               typename Dummy = std::enable_if_t<std::is_same<
                   std::remove_cv_t<value_type>, std::remove_cv_t<OtherValueType>>::value>>
-    constexpr bool operator!=(const strided_span<OtherValueType, OtherRank>& other) const noexcept
+    constexpr bool
+    operator!=(const strided_span<OtherValueType, OtherRank>& other) const GSL_NOEXCEPT
     {
         return !(*this == other);
     }
@@ -1914,7 +1921,8 @@
     template <typename OtherValueType, std::ptrdiff_t OtherRank,
               typename Dummy = std::enable_if_t<std::is_same<
                   std::remove_cv_t<value_type>, std::remove_cv_t<OtherValueType>>::value>>
-    constexpr bool operator<(const strided_span<OtherValueType, OtherRank>& other) const noexcept
+    constexpr bool
+    operator<(const strided_span<OtherValueType, OtherRank>& other) const GSL_NOEXCEPT
     {
         return std::lexicographical_compare(this->begin(), this->end(), other.begin(), other.end());
     }
@@ -1922,7 +1930,8 @@
     template <typename OtherValueType, std::ptrdiff_t OtherRank,
               typename Dummy = std::enable_if_t<std::is_same<
                   std::remove_cv_t<value_type>, std::remove_cv_t<OtherValueType>>::value>>
-    constexpr bool operator<=(const strided_span<OtherValueType, OtherRank>& other) const noexcept
+    constexpr bool
+    operator<=(const strided_span<OtherValueType, OtherRank>& other) const GSL_NOEXCEPT
     {
         return !(other < *this);
     }
@@ -1930,7 +1939,8 @@
     template <typename OtherValueType, std::ptrdiff_t OtherRank,
               typename Dummy = std::enable_if_t<std::is_same<
                   std::remove_cv_t<value_type>, std::remove_cv_t<OtherValueType>>::value>>
-    constexpr bool operator>(const strided_span<OtherValueType, OtherRank>& other) const noexcept
+    constexpr bool
+    operator>(const strided_span<OtherValueType, OtherRank>& other) const GSL_NOEXCEPT
     {
         return (other < *this);
     }
@@ -1938,7 +1948,8 @@
     template <typename OtherValueType, std::ptrdiff_t OtherRank,
               typename Dummy = std::enable_if_t<std::is_same<
                   std::remove_cv_t<value_type>, std::remove_cv_t<OtherValueType>>::value>>
-    constexpr bool operator>=(const strided_span<OtherValueType, OtherRank>& other) const noexcept
+    constexpr bool
+    operator>=(const strided_span<OtherValueType, OtherRank>& other) const GSL_NOEXCEPT
     {
         return !(*this < other);
     }
@@ -1956,7 +1967,7 @@
     }
 
     template <bool Enabled = (Rank == 1), typename Dummy = std::enable_if_t<Enabled>>
-    static index_type resize_stride(const index_type& strides, std::ptrdiff_t, void* = 0)
+    static index_type resize_stride(const index_type& strides, std::ptrdiff_t, void* = nullptr)
     {
         // Only strided arrays with regular strides can be resized
         Expects(strides[Rank - 1] == 1);
@@ -1973,7 +1984,7 @@
         // memory that can contain a multiple of new type elements
         Expects(strides[Rank - 2] >= d && (strides[Rank - 2] % d == 0));
 
-        for (size_t i = Rank - 1; i > 0; --i) {
+        for (std::size_t i = Rank - 1; i > 0; --i) {
             // Only strided arrays with regular strides can be resized
             Expects((strides[i - 1] >= strides[i]) && (strides[i - 1] % strides[i] == 0));
         }
@@ -2014,75 +2025,84 @@
     }
 
 public:
-    reference operator*() const noexcept
+    reference operator*() const GSL_NOEXCEPT
     {
         validateThis();
         return *data_;
     }
-    pointer operator->() const noexcept
+    pointer operator->() const GSL_NOEXCEPT
     {
         validateThis();
         return data_;
     }
-    contiguous_span_iterator& operator++() noexcept
+    contiguous_span_iterator& operator++() GSL_NOEXCEPT
     {
         ++data_;
         return *this;
     }
-    contiguous_span_iterator operator++(int) noexcept
+    contiguous_span_iterator operator++(int) GSL_NOEXCEPT
     {
         auto ret = *this;
         ++(*this);
         return ret;
     }
-    contiguous_span_iterator& operator--() noexcept
+    contiguous_span_iterator& operator--() GSL_NOEXCEPT
     {
         --data_;
         return *this;
     }
-    contiguous_span_iterator operator--(int) noexcept
+    contiguous_span_iterator operator--(int) GSL_NOEXCEPT
     {
         auto ret = *this;
         --(*this);
         return ret;
     }
-    contiguous_span_iterator operator+(difference_type n) const noexcept
+    contiguous_span_iterator operator+(difference_type n) const GSL_NOEXCEPT
     {
         contiguous_span_iterator ret{*this};
         return ret += n;
     }
-    contiguous_span_iterator& operator+=(difference_type n) noexcept
+    contiguous_span_iterator& operator+=(difference_type n) GSL_NOEXCEPT
     {
         data_ += n;
         return *this;
     }
-    contiguous_span_iterator operator-(difference_type n) const noexcept
+    contiguous_span_iterator operator-(difference_type n) const GSL_NOEXCEPT
     {
         contiguous_span_iterator ret{*this};
         return ret -= n;
     }
-    contiguous_span_iterator& operator-=(difference_type n) noexcept { return *this += -n; }
-    difference_type operator-(const contiguous_span_iterator& rhs) const noexcept
+    contiguous_span_iterator& operator-=(difference_type n) GSL_NOEXCEPT { return *this += -n; }
+    difference_type operator-(const contiguous_span_iterator& rhs) const GSL_NOEXCEPT
     {
         Expects(m_validator == rhs.m_validator);
         return data_ - rhs.data_;
     }
-    reference operator[](difference_type n) const noexcept { return *(*this + n); }
-    bool operator==(const contiguous_span_iterator& rhs) const noexcept
+    reference operator[](difference_type n) const GSL_NOEXCEPT { return *(*this + n); }
+    bool operator==(const contiguous_span_iterator& rhs) const GSL_NOEXCEPT
     {
         Expects(m_validator == rhs.m_validator);
         return data_ == rhs.data_;
     }
-    bool operator!=(const contiguous_span_iterator& rhs) const noexcept { return !(*this == rhs); }
-    bool operator<(const contiguous_span_iterator& rhs) const noexcept
+    bool operator!=(const contiguous_span_iterator& rhs) const GSL_NOEXCEPT
+    {
+        return !(*this == rhs);
+    }
+    bool operator<(const contiguous_span_iterator& rhs) const GSL_NOEXCEPT
     {
         Expects(m_validator == rhs.m_validator);
         return data_ < rhs.data_;
     }
-    bool operator<=(const contiguous_span_iterator& rhs) const noexcept { return !(rhs < *this); }
-    bool operator>(const contiguous_span_iterator& rhs) const noexcept { return rhs < *this; }
-    bool operator>=(const contiguous_span_iterator& rhs) const noexcept { return !(rhs > *this); }
-    void swap(contiguous_span_iterator& rhs) noexcept
+    bool operator<=(const contiguous_span_iterator& rhs) const GSL_NOEXCEPT
+    {
+        return !(rhs < *this);
+    }
+    bool operator>(const contiguous_span_iterator& rhs) const GSL_NOEXCEPT { return rhs < *this; }
+    bool operator>=(const contiguous_span_iterator& rhs) const GSL_NOEXCEPT
+    {
+        return !(rhs > *this);
+    }
+    void swap(contiguous_span_iterator& rhs) GSL_NOEXCEPT
     {
         std::swap(data_, rhs.data_);
         std::swap(m_validator, rhs.m_validator);
@@ -2091,7 +2111,7 @@
 
 template <typename Span>
 contiguous_span_iterator<Span> operator+(typename contiguous_span_iterator<Span>::difference_type n,
-                                         const contiguous_span_iterator<Span>& rhs) noexcept
+                                         const contiguous_span_iterator<Span>& rhs) GSL_NOEXCEPT
 {
     return rhs + n;
 }
@@ -2109,7 +2129,7 @@
     using typename Base::value_type;
 
 private:
-    template <typename ValueType, size_t Rank>
+    template <typename ValueType, std::size_t Rank>
     friend class strided_span;
 
     const Span* m_container;
@@ -2121,68 +2141,68 @@
     }
 
 public:
-    reference operator*() noexcept { return (*m_container)[*m_itr]; }
-    pointer operator->() noexcept { return &(*m_container)[*m_itr]; }
-    general_span_iterator& operator++() noexcept
+    reference operator*() GSL_NOEXCEPT { return (*m_container)[*m_itr]; }
+    pointer operator->() GSL_NOEXCEPT { return &(*m_container)[*m_itr]; }
+    general_span_iterator& operator++() GSL_NOEXCEPT
     {
         ++m_itr;
         return *this;
     }
-    general_span_iterator operator++(int) noexcept
+    general_span_iterator operator++(int) GSL_NOEXCEPT
     {
         auto ret = *this;
         ++(*this);
         return ret;
     }
-    general_span_iterator& operator--() noexcept
+    general_span_iterator& operator--() GSL_NOEXCEPT
     {
         --m_itr;
         return *this;
     }
-    general_span_iterator operator--(int) noexcept
+    general_span_iterator operator--(int) GSL_NOEXCEPT
     {
         auto ret = *this;
         --(*this);
         return ret;
     }
-    general_span_iterator operator+(difference_type n) const noexcept
+    general_span_iterator operator+(difference_type n) const GSL_NOEXCEPT
     {
         general_span_iterator ret{*this};
         return ret += n;
     }
-    general_span_iterator& operator+=(difference_type n) noexcept
+    general_span_iterator& operator+=(difference_type n) GSL_NOEXCEPT
     {
         m_itr += n;
         return *this;
     }
-    general_span_iterator operator-(difference_type n) const noexcept
+    general_span_iterator operator-(difference_type n) const GSL_NOEXCEPT
     {
         general_span_iterator ret{*this};
         return ret -= n;
     }
-    general_span_iterator& operator-=(difference_type n) noexcept { return *this += -n; }
-    difference_type operator-(const general_span_iterator& rhs) const noexcept
+    general_span_iterator& operator-=(difference_type n) GSL_NOEXCEPT { return *this += -n; }
+    difference_type operator-(const general_span_iterator& rhs) const GSL_NOEXCEPT
     {
         Expects(m_container == rhs.m_container);
         return m_itr - rhs.m_itr;
     }
-    value_type operator[](difference_type n) const noexcept { return (*m_container)[m_itr[n]]; }
+    value_type operator[](difference_type n) const GSL_NOEXCEPT { return (*m_container)[m_itr[n]]; }
 
-    bool operator==(const general_span_iterator& rhs) const noexcept
+    bool operator==(const general_span_iterator& rhs) const GSL_NOEXCEPT
     {
         Expects(m_container == rhs.m_container);
         return m_itr == rhs.m_itr;
     }
-    bool operator!=(const general_span_iterator& rhs) const noexcept { return !(*this == rhs); }
-    bool operator<(const general_span_iterator& rhs) const noexcept
+    bool operator!=(const general_span_iterator& rhs) const GSL_NOEXCEPT { return !(*this == rhs); }
+    bool operator<(const general_span_iterator& rhs) const GSL_NOEXCEPT
     {
         Expects(m_container == rhs.m_container);
         return m_itr < rhs.m_itr;
     }
-    bool operator<=(const general_span_iterator& rhs) const noexcept { return !(rhs < *this); }
-    bool operator>(const general_span_iterator& rhs) const noexcept { return rhs < *this; }
-    bool operator>=(const general_span_iterator& rhs) const noexcept { return !(rhs > *this); }
-    void swap(general_span_iterator& rhs) noexcept
+    bool operator<=(const general_span_iterator& rhs) const GSL_NOEXCEPT { return !(rhs < *this); }
+    bool operator>(const general_span_iterator& rhs) const GSL_NOEXCEPT { return rhs < *this; }
+    bool operator>=(const general_span_iterator& rhs) const GSL_NOEXCEPT { return !(rhs > *this); }
+    void swap(general_span_iterator& rhs) GSL_NOEXCEPT
     {
         std::swap(m_itr, rhs.m_itr);
         std::swap(m_container, rhs.m_container);
@@ -2191,41 +2211,24 @@
 
 template <typename Span>
 general_span_iterator<Span> operator+(typename general_span_iterator<Span>::difference_type n,
-                                      const general_span_iterator<Span>& rhs) noexcept
+                                      const general_span_iterator<Span>& rhs) GSL_NOEXCEPT
 {
     return rhs + n;
 }
 
 } // namespace gsl
 
+#undef GSL_NOEXCEPT
+
 #ifdef _MSC_VER
+#if _MSC_VER < 1910
 
 #undef constexpr
 #pragma pop_macro("constexpr")
+#endif // _MSC_VER < 1910
 
-#if _MSC_VER <= 1800
 #pragma warning(pop)
 
-#ifndef GSL_THROW_ON_CONTRACT_VIOLATION
-#undef noexcept
-#pragma pop_macro("noexcept")
-#endif // GSL_THROW_ON_CONTRACT_VIOLATION
-
-#undef GSL_MSVC_HAS_VARIADIC_CTOR_BUG
-
-#endif // _MSC_VER <= 1800
-
 #endif // _MSC_VER
 
-#if defined(GSL_THROW_ON_CONTRACT_VIOLATION)
-
-#undef noexcept
-
-#ifdef _MSC_VER
-#pragma warning(pop)
-#pragma pop_macro("noexcept")
-#endif
-
-#endif // GSL_THROW_ON_CONTRACT_VIOLATION
-
 #endif // GSL_MULTI_SPAN_H
diff --git a/gsl/span b/include/gsl/span
similarity index 66%
rename from gsl/span
rename to include/gsl/span
index cf90e86..3f54b48 100644
--- a/gsl/span
+++ b/include/gsl/span
@@ -1,4 +1,3 @@
-
 ///////////////////////////////////////////////////////////////////////////////
 //
 // Copyright (c) 2015 Microsoft Corporation. All rights reserved.
@@ -20,65 +19,41 @@
 #ifndef GSL_SPAN_H
 #define GSL_SPAN_H
 
-#include "gsl_assert"
-#include "gsl_byte"
-#include "gsl_util"
+#include <gsl/gsl_assert>
+#include <gsl/gsl_byte>
+#include <gsl/gsl_util>
+
 #include <array>
 #include <iterator>
 #include <limits>
+#include <memory>
 #include <stdexcept>
 #include <type_traits>
 #include <utility>
 
 #ifdef _MSC_VER
-
 #pragma warning(push)
 
 // turn off some warnings that are noisy about our Expects statements
 #pragma warning(disable : 4127) // conditional expression is constant
+#pragma warning(disable : 4702) // unreachable code
 
 // blanket turn off warnings from CppCoreCheck for now
 // so people aren't annoyed by them when running the tool.
 // more targeted suppressions will be added in a future update to the GSL
 #pragma warning(disable : 26481 26482 26483 26485 26490 26491 26492 26493 26495)
 
-// No MSVC does constexpr fully yet
+#if _MSC_VER < 1910
 #pragma push_macro("constexpr")
 #define constexpr /*constexpr*/
 
-// VS 2013 workarounds
-#if _MSC_VER <= 1800
-
-#define GSL_MSVC_HAS_VARIADIC_CTOR_BUG
-#define GSL_MSVC_NO_DEFAULT_MOVE_CTOR
-#define GSL_MSVC_NO_CPP14_STD_EQUAL
-
-// noexcept is not understood
-#ifndef GSL_THROW_ON_CONTRACT_VIOLATION
-#pragma push_macro("noexcept")
-#define noexcept /*noexcept*/
-#endif
-
-#pragma push_macro("alignof")
-#define alignof __alignof
-
-// turn off some misguided warnings
-#pragma warning(push)
-#pragma warning(disable : 4351) // warns about newly introduced aggregate initializer behavior
-#pragma warning(disable : 4512) // warns that assignment op could not be generated
-
-#endif // _MSC_VER <= 1800
-
-#endif // _MSC_VER
+#endif                          // _MSC_VER < 1910
+#endif                          // _MSC_VER
 
 #ifdef GSL_THROW_ON_CONTRACT_VIOLATION
-
-#ifdef _MSC_VER
-#pragma push_macro("noexcept")
-#endif
-
-#define noexcept /*noexcept*/
-
+#define GSL_NOEXCEPT /*noexcept*/
+#else
+#define GSL_NOEXCEPT noexcept
 #endif // GSL_THROW_ON_CONTRACT_VIOLATION
 
 namespace gsl
@@ -113,7 +88,7 @@
     {
     };
 
-    template <class ElementType, size_t Extent>
+    template <class ElementType, std::size_t Extent>
     struct is_std_array_oracle<std::array<ElementType, Extent>> : std::true_type
     {
     };
@@ -139,159 +114,157 @@
     template <class Span, bool IsConst>
     class span_iterator
     {
+        using element_type_ = typename Span::element_type;
+
     public:
         using iterator_category = std::random_access_iterator_tag;
-        using value_type =
-            std::conditional_t<IsConst, std::add_const_t<typename Span::element_type>,
-                               typename Span::element_type>;
+        using value_type = std::remove_cv_t<element_type_>;
         using difference_type = typename Span::index_type;
 
-        using pointer = std::add_pointer_t<value_type>;
-        using reference = std::add_lvalue_reference_t<value_type>;
+        using reference = std::conditional_t<IsConst, const element_type_, element_type_>&;
+        using pointer = std::add_pointer_t<reference>;
 
-        constexpr span_iterator() noexcept : span_iterator(nullptr, 0) {}
+        span_iterator() = default;
 
-        constexpr span_iterator(const Span* span, typename Span::index_type index)
+        constexpr span_iterator(const Span* span, typename Span::index_type index) GSL_NOEXCEPT
             : span_(span), index_(index)
         {
             Expects(span == nullptr || (index_ >= 0 && index <= span_->length()));
         }
 
-        friend class span_iterator<Span, true>;
-        constexpr span_iterator(const span_iterator<Span, false>& other) noexcept
+        friend span_iterator<Span, true>;
+        template<bool B, std::enable_if_t<!B && IsConst>* = nullptr>
+        constexpr span_iterator(const span_iterator<Span, B>& other) GSL_NOEXCEPT
             : span_iterator(other.span_, other.index_)
         {
         }
 
-        constexpr span_iterator<Span, IsConst>& operator=(const span_iterator<Span, IsConst>&) noexcept = default;
-
-        constexpr reference operator*() const
+        constexpr reference operator*() const GSL_NOEXCEPT
         {
             Expects(span_);
             return (*span_)[index_];
         }
 
-        constexpr pointer operator->() const
+        constexpr pointer operator->() const GSL_NOEXCEPT
         {
-            Expects(span_);
-            return &((*span_)[index_]);
+            Expects(span_ && index_ >= 0 && index_ < span_->length());
+            return span_->data() + index_;
         }
 
-        constexpr span_iterator& operator++() noexcept
+        constexpr span_iterator& operator++() GSL_NOEXCEPT
         {
             Expects(span_ && index_ >= 0 && index_ < span_->length());
             ++index_;
             return *this;
         }
 
-        constexpr span_iterator operator++(int) noexcept
+        constexpr span_iterator operator++(int) GSL_NOEXCEPT
         {
             auto ret = *this;
             ++(*this);
             return ret;
         }
 
-        constexpr span_iterator& operator--() noexcept
+        constexpr span_iterator& operator--() GSL_NOEXCEPT
         {
             Expects(span_ && index_ > 0 && index_ <= span_->length());
             --index_;
             return *this;
         }
 
-        constexpr span_iterator operator--(int) noexcept
+        constexpr span_iterator operator--(int) GSL_NOEXCEPT
         {
             auto ret = *this;
             --(*this);
             return ret;
         }
 
-        constexpr span_iterator operator+(difference_type n) const noexcept
+        constexpr span_iterator operator+(difference_type n) const GSL_NOEXCEPT
         {
             auto ret = *this;
             return ret += n;
         }
 
-        constexpr span_iterator& operator+=(difference_type n) noexcept
+        constexpr span_iterator& operator+=(difference_type n) GSL_NOEXCEPT
         {
             Expects(span_ && (index_ + n) >= 0 && (index_ + n) <= span_->length());
             index_ += n;
             return *this;
         }
 
-        constexpr span_iterator operator-(difference_type n) const noexcept
+        constexpr span_iterator operator-(difference_type n) const GSL_NOEXCEPT
         {
             auto ret = *this;
             return ret -= n;
         }
 
-        constexpr span_iterator& operator-=(difference_type n) noexcept { return *this += -n; }
+        constexpr span_iterator& operator-=(difference_type n) GSL_NOEXCEPT { return *this += -n; }
 
-        constexpr difference_type operator-(const span_iterator& rhs) const noexcept
+        constexpr difference_type operator-(const span_iterator& rhs) const GSL_NOEXCEPT
         {
             Expects(span_ == rhs.span_);
             return index_ - rhs.index_;
         }
 
-        constexpr reference operator[](difference_type n) const noexcept { return *(*this + n); }
+        constexpr reference operator[](difference_type n) const GSL_NOEXCEPT
+        {
+            return *(*this + n);
+        }
 
         constexpr friend bool operator==(const span_iterator& lhs,
-                                         const span_iterator& rhs) noexcept
+                                         const span_iterator& rhs) GSL_NOEXCEPT
         {
             return lhs.span_ == rhs.span_ && lhs.index_ == rhs.index_;
         }
 
         constexpr friend bool operator!=(const span_iterator& lhs,
-                                         const span_iterator& rhs) noexcept
+                                         const span_iterator& rhs) GSL_NOEXCEPT
         {
             return !(lhs == rhs);
         }
 
-        constexpr friend bool operator<(const span_iterator& lhs, const span_iterator& rhs) noexcept
+        constexpr friend bool operator<(const span_iterator& lhs,
+                                        const span_iterator& rhs) GSL_NOEXCEPT
         {
             Expects(lhs.span_ == rhs.span_);
             return lhs.index_ < rhs.index_;
         }
 
         constexpr friend bool operator<=(const span_iterator& lhs,
-                                         const span_iterator& rhs) noexcept
+                                         const span_iterator& rhs) GSL_NOEXCEPT
         {
             return !(rhs < lhs);
         }
 
-        constexpr friend bool operator>(const span_iterator& lhs, const span_iterator& rhs) noexcept
+        constexpr friend bool operator>(const span_iterator& lhs,
+                                        const span_iterator& rhs) GSL_NOEXCEPT
         {
             return rhs < lhs;
         }
 
         constexpr friend bool operator>=(const span_iterator& lhs,
-                                         const span_iterator& rhs) noexcept
+                                         const span_iterator& rhs) GSL_NOEXCEPT
         {
             return !(rhs > lhs);
         }
 
-        void swap(span_iterator& rhs) noexcept
-        {
-            std::swap(index_, rhs.index_);
-            std::swap(span_, rhs.span_);
-        }
-
     protected:
-        const Span* span_;
-        std::ptrdiff_t index_;
+        const Span* span_ = nullptr;
+        std::ptrdiff_t index_ = 0;
     };
 
     template <class Span, bool IsConst>
-    constexpr span_iterator<Span, IsConst>
+    inline constexpr span_iterator<Span, IsConst>
     operator+(typename span_iterator<Span, IsConst>::difference_type n,
-              const span_iterator<Span, IsConst>& rhs) noexcept
+              const span_iterator<Span, IsConst>& rhs) GSL_NOEXCEPT
     {
         return rhs + n;
     }
 
     template <class Span, bool IsConst>
-    constexpr span_iterator<Span, IsConst>
+    inline constexpr span_iterator<Span, IsConst>
     operator-(typename span_iterator<Span, IsConst>::difference_type n,
-              const span_iterator<Span, IsConst>& rhs) noexcept
+              const span_iterator<Span, IsConst>& rhs) GSL_NOEXCEPT
     {
         return rhs - n;
     }
@@ -304,10 +277,10 @@
 
         static_assert(Ext >= 0, "A fixed-size span must be >= 0 in size.");
 
-        constexpr extent_type() noexcept {}
+        constexpr extent_type() GSL_NOEXCEPT {}
 
         template <index_type Other>
-        constexpr extent_type(extent_type<Other> ext) noexcept
+        constexpr extent_type(extent_type<Other> ext)
         {
             static_assert(Other == Ext || Other == dynamic_extent,
                           "Mismatch between fixed-size extent and size of initializing data.");
@@ -316,7 +289,7 @@
 
         constexpr extent_type(index_type size) { Expects(size == Ext); }
 
-        constexpr inline index_type size() const noexcept { return Ext; }
+        constexpr index_type size() const GSL_NOEXCEPT { return Ext; }
     };
 
     template <>
@@ -332,7 +305,7 @@
 
         explicit constexpr extent_type(index_type size) : size_(size) { Expects(size >= 0); }
 
-        constexpr inline index_type size() const noexcept { return size_; }
+        constexpr index_type size() const GSL_NOEXCEPT { return size_; }
 
     private:
         index_type size_;
@@ -346,6 +319,7 @@
 public:
     // constants and types
     using element_type = ElementType;
+    using value_type = std::remove_cv_t<ElementType>;
     using index_type = std::ptrdiff_t;
     using pointer = element_type*;
     using reference = element_type&;
@@ -355,12 +329,20 @@
     using reverse_iterator = std::reverse_iterator<iterator>;
     using const_reverse_iterator = std::reverse_iterator<const_iterator>;
 
+    using size_type = index_type;
+
     constexpr static const index_type extent = Extent;
 
     // [span.cons], span constructors, copy, assignment, and destructor
-    constexpr span() noexcept : storage_(nullptr, details::extent_type<0>()) {}
+    template <bool Dependent = false,
+              // "Dependent" is needed to make "std::enable_if_t<Dependent || Extent <= 0>" SFINAE,
+              // since "std::enable_if_t<Extent <= 0>" is ill-formed when Extent is greater than 0.
+              class = std::enable_if_t<(Dependent || Extent <= 0)>>
+    constexpr span() GSL_NOEXCEPT : storage_(nullptr, details::extent_type<0>())
+    {
+    }
 
-    constexpr span(std::nullptr_t) noexcept : span() {}
+    constexpr span(std::nullptr_t) GSL_NOEXCEPT : span() {}
 
     constexpr span(pointer ptr, index_type count) : storage_(ptr, count) {}
 
@@ -369,23 +351,37 @@
     {
     }
 
-    template <size_t N>
-    constexpr span(element_type (&arr)[N]) noexcept : storage_(&arr[0], details::extent_type<N>())
-    {
-    }
-
-    template <size_t N, class ArrayElementType = std::remove_const_t<element_type>>
-    constexpr span(std::array<ArrayElementType, N>& arr) noexcept
+    template <std::size_t N>
+    constexpr span(element_type (&arr)[N]) GSL_NOEXCEPT
         : storage_(&arr[0], details::extent_type<N>())
     {
     }
 
-    template <size_t N>
-    constexpr span(const std::array<std::remove_const_t<element_type>, N>& arr) noexcept
+    template <std::size_t N, class ArrayElementType = std::remove_const_t<element_type>>
+    constexpr span(std::array<ArrayElementType, N>& arr) GSL_NOEXCEPT
         : storage_(&arr[0], details::extent_type<N>())
     {
     }
 
+    template <std::size_t N>
+    constexpr span(const std::array<std::remove_const_t<element_type>, N>& arr) GSL_NOEXCEPT
+        : storage_(&arr[0], details::extent_type<N>())
+    {
+    }
+
+    template <class ArrayElementType = std::add_pointer<element_type>>
+    constexpr span(const std::unique_ptr<ArrayElementType>& ptr, index_type count)
+        : storage_(ptr.get(), count)
+    {
+    }
+
+    constexpr span(const std::unique_ptr<ElementType>& ptr) : storage_(ptr.get(), ptr.get() ? 1 : 0)
+    {
+    }
+    constexpr span(const std::shared_ptr<ElementType>& ptr) : storage_(ptr.get(), ptr.get() ? 1 : 0)
+    {
+    }
+
     // NB: the SFINAE here uses .data() as a incomplete/imperfect proxy for the requirement
     // on Container to be a contiguous sequence container.
     template <class Container,
@@ -408,12 +404,8 @@
     {
     }
 
-    constexpr span(const span& other) noexcept = default;
-#ifndef GSL_MSVC_NO_DEFAULT_MOVE_CTOR
-    constexpr span(span&& other) noexcept = default;
-#else
-    constexpr span(span&& other) noexcept : storage_(std::move(other.storage_)) {}
-#endif
+    constexpr span(const span& other) GSL_NOEXCEPT = default;
+    constexpr span(span&& other) GSL_NOEXCEPT = default;
 
     template <
         class OtherElementType, std::ptrdiff_t OtherExtent,
@@ -435,18 +427,11 @@
     {
     }
 
-    ~span() noexcept = default;
-    constexpr span& operator=(const span& other) noexcept = default;
+    ~span() GSL_NOEXCEPT = default;
+    constexpr span& operator=(const span& other) GSL_NOEXCEPT = default;
 
-#ifndef GSL_MSVC_NO_DEFAULT_MOVE_CTOR
-    constexpr span& operator=(span&& other) noexcept = default;
-#else
-    constexpr span& operator=(span&& other) noexcept
-    {
-        storage_ = std::move(other.storage_);
-        return *this;
-    }
-#endif
+    constexpr span& operator=(span&& other) GSL_NOEXCEPT = default;
+
     // [span.sub], span subviews
     template <std::ptrdiff_t Count>
     constexpr span<element_type, Count> first() const
@@ -491,11 +476,14 @@
     }
 
     // [span.obs], span observers
-    constexpr index_type length() const noexcept { return size(); }
-    constexpr index_type size() const noexcept { return storage_.size(); }
-    constexpr index_type length_bytes() const noexcept { return size_bytes(); }
-    constexpr index_type size_bytes() const noexcept { return size() * sizeof(element_type); }
-    constexpr bool empty() const noexcept { return size() == 0; }
+    constexpr index_type length() const GSL_NOEXCEPT { return size(); }
+    constexpr index_type size() const GSL_NOEXCEPT { return storage_.size(); }
+    constexpr index_type length_bytes() const GSL_NOEXCEPT { return size_bytes(); }
+    constexpr index_type size_bytes() const GSL_NOEXCEPT
+    {
+        return size() * narrow_cast<index_type>(sizeof(element_type));
+    }
+    constexpr bool empty() const GSL_NOEXCEPT { return size() == 0; }
 
     // [span.elem], span element access
     constexpr reference operator[](index_type idx) const
@@ -506,20 +494,20 @@
 
     constexpr reference at(index_type idx) const { return this->operator[](idx); }
     constexpr reference operator()(index_type idx) const { return this->operator[](idx); }
-    constexpr pointer data() const noexcept { return storage_.data(); }
+    constexpr pointer data() const GSL_NOEXCEPT { return storage_.data(); }
 
     // [span.iter], span iterator support
-    iterator begin() const noexcept { return {this, 0}; }
-    iterator end() const noexcept { return {this, length()}; }
+    iterator begin() const GSL_NOEXCEPT { return {this, 0}; }
+    iterator end() const GSL_NOEXCEPT { return {this, length()}; }
 
-    const_iterator cbegin() const noexcept { return {this, 0}; }
-    const_iterator cend() const noexcept { return {this, length()}; }
+    const_iterator cbegin() const GSL_NOEXCEPT { return {this, 0}; }
+    const_iterator cend() const GSL_NOEXCEPT { return {this, length()}; }
 
-    reverse_iterator rbegin() const noexcept { return reverse_iterator{end()}; }
-    reverse_iterator rend() const noexcept { return reverse_iterator{begin()}; }
+    reverse_iterator rbegin() const GSL_NOEXCEPT { return reverse_iterator{end()}; }
+    reverse_iterator rend() const GSL_NOEXCEPT { return reverse_iterator{begin()}; }
 
-    const_reverse_iterator crbegin() const noexcept { return const_reverse_iterator{cend()}; }
-    const_reverse_iterator crend() const noexcept { return const_reverse_iterator{cbegin()}; }
+    const_reverse_iterator crbegin() const GSL_NOEXCEPT { return const_reverse_iterator{cend()}; }
+    const_reverse_iterator crend() const GSL_NOEXCEPT { return const_reverse_iterator{cbegin()}; }
 
 private:
     // this implementation detail class lets us take advantage of the
@@ -535,7 +523,7 @@
             Expects((!data && ExtentType::size() == 0) || (data && ExtentType::size() >= 0));
         }
 
-        constexpr inline pointer data() const noexcept { return data_; }
+        constexpr pointer data() const GSL_NOEXCEPT { return data_; }
 
     private:
         pointer data_;
@@ -546,42 +534,43 @@
 
 // [span.comparison], span comparison operators
 template <class ElementType, std::ptrdiff_t FirstExtent, std::ptrdiff_t SecondExtent>
-constexpr bool operator==(const span<ElementType, FirstExtent>& l,
-                          const span<ElementType, SecondExtent>& r)
+inline constexpr bool operator==(const span<ElementType, FirstExtent>& l,
+                                 const span<ElementType, SecondExtent>& r)
 {
-#ifdef GSL_MSVC_NO_CPP14_STD_EQUAL
-    return (l.size() == r.size()) && std::equal(l.begin(), l.end(), r.begin());
-#else
     return std::equal(l.begin(), l.end(), r.begin(), r.end());
-#endif
 }
 
 template <class ElementType, std::ptrdiff_t Extent>
-constexpr bool operator!=(const span<ElementType, Extent>& l, const span<ElementType, Extent>& r)
+inline constexpr bool operator!=(const span<ElementType, Extent>& l,
+                                 const span<ElementType, Extent>& r)
 {
     return !(l == r);
 }
 
 template <class ElementType, std::ptrdiff_t Extent>
-constexpr bool operator<(const span<ElementType, Extent>& l, const span<ElementType, Extent>& r)
+inline constexpr bool operator<(const span<ElementType, Extent>& l,
+                                const span<ElementType, Extent>& r)
 {
     return std::lexicographical_compare(l.begin(), l.end(), r.begin(), r.end());
 }
 
 template <class ElementType, std::ptrdiff_t Extent>
-constexpr bool operator<=(const span<ElementType, Extent>& l, const span<ElementType, Extent>& r)
+inline constexpr bool operator<=(const span<ElementType, Extent>& l,
+                                 const span<ElementType, Extent>& r)
 {
     return !(l > r);
 }
 
 template <class ElementType, std::ptrdiff_t Extent>
-constexpr bool operator>(const span<ElementType, Extent>& l, const span<ElementType, Extent>& r)
+inline constexpr bool operator>(const span<ElementType, Extent>& l,
+                                const span<ElementType, Extent>& r)
 {
     return r < l;
 }
 
 template <class ElementType, std::ptrdiff_t Extent>
-constexpr bool operator>=(const span<ElementType, Extent>& l, const span<ElementType, Extent>& r)
+inline constexpr bool operator>=(const span<ElementType, Extent>& l,
+                                 const span<ElementType, Extent>& r)
 {
     return !(l < r);
 }
@@ -591,7 +580,7 @@
     // if we only supported compilers with good constexpr support then
     // this pair of classes could collapse down to a constexpr function
 
-    // we should use a narrow_cast<> to go to size_t, but older compilers may not see it as
+    // we should use a narrow_cast<> to go to std::size_t, but older compilers may not see it as
     // constexpr
     // and so will fail compilation of the template
     template <class ElementType, std::ptrdiff_t Extent>
@@ -612,7 +601,7 @@
 // [span.objectrep], views of object representation
 template <class ElementType, std::ptrdiff_t Extent>
 span<const byte, details::calculate_byte_size<ElementType, Extent>::value>
-as_bytes(span<ElementType, Extent> s) noexcept
+as_bytes(span<ElementType, Extent> s) GSL_NOEXCEPT
 {
     return {reinterpret_cast<const byte*>(s.data()), s.size_bytes()};
 }
@@ -620,14 +609,59 @@
 template <class ElementType, std::ptrdiff_t Extent,
           class = std::enable_if_t<!std::is_const<ElementType>::value>>
 span<byte, details::calculate_byte_size<ElementType, Extent>::value>
-as_writeable_bytes(span<ElementType, Extent> s) noexcept
+as_writeable_bytes(span<ElementType, Extent> s) GSL_NOEXCEPT
 {
     return {reinterpret_cast<byte*>(s.data()), s.size_bytes()};
 }
 
+//
+// make_span() - Utility functions for creating spans
+//
+template <class ElementType>
+span<ElementType> make_span(ElementType* ptr, typename span<ElementType>::index_type count)
+{
+    return span<ElementType>(ptr, count);
+}
+
+template <class ElementType>
+span<ElementType> make_span(ElementType* firstElem, ElementType* lastElem)
+{
+    return span<ElementType>(firstElem, lastElem);
+}
+
+template <class ElementType, std::size_t N>
+span<ElementType, N> make_span(ElementType (&arr)[N])
+{
+    return span<ElementType, N>(arr);
+}
+
+template <class Container>
+span<typename Container::value_type> make_span(Container& cont)
+{
+    return span<typename Container::value_type>(cont);
+}
+
+template <class Container>
+span<const typename Container::value_type> make_span(const Container& cont)
+{
+    return span<const typename Container::value_type>(cont);
+}
+
+template <class Ptr>
+span<typename Ptr::element_type> make_span(Ptr& cont, std::ptrdiff_t count)
+{
+    return span<typename Ptr::element_type>(cont, count);
+}
+
+template <class Ptr>
+span<typename Ptr::element_type> make_span(Ptr& cont)
+{
+    return span<typename Ptr::element_type>(cont);
+}
+
 // Specialization of gsl::at for span
 template <class ElementType, std::ptrdiff_t Extent>
-constexpr ElementType& at(const span<ElementType, Extent>& s, size_t index)
+inline constexpr ElementType& at(const span<ElementType, Extent>& s, std::ptrdiff_t index)
 {
     // No bounds checking here because it is done in span::operator[] called below
     return s[index];
@@ -635,39 +669,16 @@
 
 } // namespace gsl
 
-#ifdef _MSC_VER
+#undef GSL_NOEXCEPT
 
+#ifdef _MSC_VER
+#if _MSC_VER < 1910
 #undef constexpr
 #pragma pop_macro("constexpr")
 
-#if _MSC_VER <= 1800
+#endif // _MSC_VER < 1910
+
 #pragma warning(pop)
-
-#ifndef GSL_THROW_ON_CONTRACT_VIOLATION
-#undef noexcept
-#pragma pop_macro("noexcept")
-#endif // GSL_THROW_ON_CONTRACT_VIOLATION
-
-#pragma pop_macro("alignof")
-
-#undef GSL_MSVC_HAS_VARIADIC_CTOR_BUG
-
-#endif // _MSC_VER <= 1800
-
 #endif // _MSC_VER
 
-#if defined(GSL_THROW_ON_CONTRACT_VIOLATION)
-
-#undef noexcept
-
-#ifdef _MSC_VER
-#pragma pop_macro("noexcept")
-#endif
-
-#endif // GSL_THROW_ON_CONTRACT_VIOLATION
-
-#ifdef _MSC_VER
-#pragma warning(pop)
-#endif
-
 #endif // GSL_SPAN_H
diff --git a/gsl/string_span b/include/gsl/string_span
similarity index 79%
rename from gsl/string_span
rename to include/gsl/string_span
index 703bc01..26ae3e8 100644
--- a/gsl/string_span
+++ b/include/gsl/string_span
@@ -19,19 +19,15 @@
 #ifndef GSL_STRING_SPAN_H
 #define GSL_STRING_SPAN_H
 
-#include "gsl_assert"
-#include "gsl_util"
-#include "span"
+#include <gsl/gsl_assert>
+#include <gsl/gsl_util>
+#include <gsl/span>
+
 #include <cstdint>
 #include <cstring>
 #include <string>
 
 #ifdef _MSC_VER
-
-// No MSVC does constexpr fully yet
-#pragma push_macro("constexpr")
-#define constexpr /*constexpr*/
-
 #pragma warning(push)
 
 // blanket turn off warnings from CppCoreCheck for now
@@ -39,32 +35,18 @@
 // more targeted suppressions will be added in a future update to the GSL
 #pragma warning(disable : 26481 26482 26483 26485 26490 26491 26492 26493 26495)
 
-// VS 2013 workarounds
-#if _MSC_VER <= 1800
+#if _MSC_VER < 1910
+#pragma push_macro("constexpr")
+#define constexpr /*constexpr*/
 
-#define GSL_MSVC_HAS_TYPE_DEDUCTION_BUG
-#define GSL_MSVC_HAS_SFINAE_SUBSTITUTION_ICE
-#define GSL_MSVC_NO_CPP14_STD_EQUAL
-#define GSL_MSVC_NO_DEFAULT_MOVE_CTOR
-
-// noexcept is not understood
-#ifndef GSL_THROW_ON_CONTRACT_VIOLATION
-#pragma push_macro("noexcept")
-#define noexcept /*noexcept*/
-#endif
-
-#endif // _MSC_VER <= 1800
-#endif // _MSC_VER
+#endif           // _MSC_VER < 1910
+#endif           // _MSC_VER
 
 // In order to test the library, we need it to throw exceptions that we can catch
 #ifdef GSL_THROW_ON_CONTRACT_VIOLATION
-
-#ifdef _MSC_VER
-#pragma push_macro("noexcept")
-#endif
-
-#define noexcept /*noexcept*/
-
+#define GSL_NOEXCEPT /*noexcept*/
+#else
+#define GSL_NOEXCEPT noexcept
 #endif // GSL_THROW_ON_CONTRACT_VIOLATION
 
 namespace gsl
@@ -72,7 +54,7 @@
 //
 // czstring and wzstring
 //
-// These are "tag" typedef's for C-style strings (i.e. null-terminated character arrays)
+// These are "tag" typedefs for C-style strings (i.e. null-terminated character arrays)
 // that allow static analysis to help find bugs.
 //
 // There are no additional features/semantics that we can find a way to add inside the
@@ -97,30 +79,26 @@
 
 namespace details
 {
-    inline std::ptrdiff_t string_length(const char *str, std::ptrdiff_t n)
+    inline std::ptrdiff_t string_length(const char* str, std::ptrdiff_t n)
     {
-        if (str == nullptr || n <= 0)
-            return 0;
+        if (str == nullptr || n <= 0) return 0;
 
         span<const char> str_span{str, n};
 
         std::ptrdiff_t len = 0;
-        while (len < n && str_span[len])
-            len++;
+        while (len < n && str_span[len]) len++;
 
         return len;
     }
 
-    inline std::ptrdiff_t wstring_length(const wchar_t *str, std::ptrdiff_t n)
+    inline std::ptrdiff_t wstring_length(const wchar_t* str, std::ptrdiff_t n)
     {
-        if (str == nullptr || n <= 0)
-            return 0;
+        if (str == nullptr || n <= 0) return 0;
 
         span<const wchar_t> str_span{str, n};
 
         std::ptrdiff_t len = 0;
-        while (len < n && str_span[len])
-            len++;
+        while (len < n && str_span[len]) len++;
 
         return len;
     }
@@ -184,7 +162,7 @@
     return {sz, len};
 }
 
-template <typename T, size_t N>
+template <typename T, std::size_t N>
 span<T, dynamic_extent> ensure_z(T (&sz)[N])
 {
     return ensure_z(&sz[0], static_cast<std::ptrdiff_t>(N));
@@ -225,7 +203,7 @@
     template <>
     struct length_func<char>
     {
-        std::ptrdiff_t operator()(char* const ptr, std::ptrdiff_t length) noexcept
+        std::ptrdiff_t operator()(const char* const ptr, std::ptrdiff_t length) GSL_NOEXCEPT
         {
             return details::string_length(ptr, length);
         }
@@ -234,7 +212,7 @@
     template <>
     struct length_func<wchar_t>
     {
-        std::ptrdiff_t operator()(wchar_t* const ptr, std::ptrdiff_t length) noexcept
+        std::ptrdiff_t operator()(const wchar_t* const ptr, std::ptrdiff_t length) GSL_NOEXCEPT
         {
             return details::wstring_length(ptr, length);
         }
@@ -243,7 +221,7 @@
     template <>
     struct length_func<const char>
     {
-        std::ptrdiff_t operator()(const char* const ptr, std::ptrdiff_t length) noexcept
+        std::ptrdiff_t operator()(const char* const ptr, std::ptrdiff_t length) GSL_NOEXCEPT
         {
             return details::string_length(ptr, length);
         }
@@ -252,7 +230,7 @@
     template <>
     struct length_func<const wchar_t>
     {
-        std::ptrdiff_t operator()(const wchar_t* const ptr, std::ptrdiff_t length) noexcept
+        std::ptrdiff_t operator()(const wchar_t* const ptr, std::ptrdiff_t length) GSL_NOEXCEPT
         {
             return details::wstring_length(ptr, length);
         }
@@ -279,59 +257,48 @@
     using const_reverse_iterator = typename impl_type::const_reverse_iterator;
 
     // default (empty)
-    constexpr basic_string_span() noexcept = default;
+    constexpr basic_string_span() GSL_NOEXCEPT = default;
 
     // copy
-    constexpr basic_string_span(const basic_string_span& other) noexcept = default;
+    constexpr basic_string_span(const basic_string_span& other) GSL_NOEXCEPT = default;
 
 // move
-#ifndef GSL_MSVC_NO_DEFAULT_MOVE_CTOR
-    constexpr basic_string_span(basic_string_span&& other) noexcept = default;
-#else
-    constexpr basic_string_span(basic_string_span&& other) : span_(std::move(other.span_)) {}
-#endif
+    constexpr basic_string_span(basic_string_span&& other) GSL_NOEXCEPT = default;
 
     // assign
-    constexpr basic_string_span& operator=(const basic_string_span& other) noexcept = default;
+    constexpr basic_string_span& operator=(const basic_string_span& other) GSL_NOEXCEPT = default;
 
 // move assign
-#ifndef GSL_MSVC_NO_DEFAULT_MOVE_CTOR
-    constexpr basic_string_span& operator=(basic_string_span&& other) noexcept = default;
-#else
-    constexpr basic_string_span& operator=(basic_string_span&& other) noexcept
-    {
-        span_ = std::move(other.span_);
-        return *this;
-    }
-#endif
+    constexpr basic_string_span& operator=(basic_string_span&& other) GSL_NOEXCEPT = default;
 
     // from nullptr
-    constexpr basic_string_span(std::nullptr_t ptr) noexcept : span_(ptr) {}
+    constexpr basic_string_span(std::nullptr_t ptr) GSL_NOEXCEPT : span_(ptr) {}
 
     constexpr basic_string_span(pointer ptr, index_type length) : span_(ptr, length) {}
     constexpr basic_string_span(pointer firstElem, pointer lastElem) : span_(firstElem, lastElem) {}
 
     // From static arrays - if 0-terminated, remove 0 from the view
     // All other containers allow 0s within the length, so we do not remove them
-    template <size_t N>
+    template <std::size_t N>
     constexpr basic_string_span(element_type (&arr)[N]) : span_(remove_z(arr))
     {
     }
 
-    template <size_t N, class ArrayElementType = std::remove_const_t<element_type>>
-    constexpr basic_string_span(std::array<ArrayElementType, N>& arr) noexcept : span_(arr)
+    template <std::size_t N, class ArrayElementType = std::remove_const_t<element_type>>
+    constexpr basic_string_span(std::array<ArrayElementType, N>& arr) GSL_NOEXCEPT : span_(arr)
     {
     }
 
-    template <size_t N, class ArrayElementType = std::remove_const_t<element_type>>
-    constexpr basic_string_span(const std::array<ArrayElementType, N>& arr) noexcept : span_(arr)
+    template <std::size_t N, class ArrayElementType = std::remove_const_t<element_type>>
+    constexpr basic_string_span(const std::array<ArrayElementType, N>& arr) GSL_NOEXCEPT
+        : span_(arr)
     {
     }
 
     // Container signature should work for basic_string after C++17 version exists
     template <class Traits, class Allocator>
     constexpr basic_string_span(std::basic_string<element_type, Traits, Allocator>& str)
-        : span_(&str[0], str.length())
+        : span_(&str[0], narrow_cast<std::ptrdiff_t>(str.length()))
     {
     }
 
@@ -411,23 +378,23 @@
 
     constexpr pointer data() const { return span_.data(); }
 
-    constexpr index_type length() const noexcept { return span_.size(); }
-    constexpr index_type size() const noexcept { return span_.size(); }
-    constexpr index_type size_bytes() const noexcept { return span_.size_bytes(); }
-    constexpr index_type length_bytes() const noexcept { return span_.length_bytes(); }
-    constexpr bool empty() const noexcept { return size() == 0; }
+    constexpr index_type length() const GSL_NOEXCEPT { return span_.size(); }
+    constexpr index_type size() const GSL_NOEXCEPT { return span_.size(); }
+    constexpr index_type size_bytes() const GSL_NOEXCEPT { return span_.size_bytes(); }
+    constexpr index_type length_bytes() const GSL_NOEXCEPT { return span_.length_bytes(); }
+    constexpr bool empty() const GSL_NOEXCEPT { return size() == 0; }
 
-    constexpr iterator begin() const noexcept { return span_.begin(); }
-    constexpr iterator end() const noexcept { return span_.end(); }
+    constexpr iterator begin() const GSL_NOEXCEPT { return span_.begin(); }
+    constexpr iterator end() const GSL_NOEXCEPT { return span_.end(); }
 
-    constexpr const_iterator cbegin() const noexcept { return span_.cbegin(); }
-    constexpr const_iterator cend() const noexcept { return span_.cend(); }
+    constexpr const_iterator cbegin() const GSL_NOEXCEPT { return span_.cbegin(); }
+    constexpr const_iterator cend() const GSL_NOEXCEPT { return span_.cend(); }
 
-    constexpr reverse_iterator rbegin() const noexcept { return span_.rbegin(); }
-    constexpr reverse_iterator rend() const noexcept { return span_.rend(); }
+    constexpr reverse_iterator rbegin() const GSL_NOEXCEPT { return span_.rbegin(); }
+    constexpr reverse_iterator rend() const GSL_NOEXCEPT { return span_.rend(); }
 
-    constexpr const_reverse_iterator crbegin() const noexcept { return span_.crbegin(); }
-    constexpr const_reverse_iterator crend() const noexcept { return span_.crend(); }
+    constexpr const_reverse_iterator crbegin() const GSL_NOEXCEPT { return span_.crbegin(); }
+    constexpr const_reverse_iterator crend() const GSL_NOEXCEPT { return span_.crend(); }
 
 private:
     static impl_type remove_z(pointer const& sz, std::ptrdiff_t max)
@@ -435,7 +402,7 @@
         return {sz, details::length_func<element_type>()(sz, max)};
     }
 
-    template <size_t N>
+    template <std::size_t N>
     static impl_type remove_z(element_type (&sz)[N])
     {
         return remove_z(&sz[0], narrow_cast<std::ptrdiff_t>(N));
@@ -459,49 +426,21 @@
 //
 // to_string() allow (explicit) conversions from string_span to string
 //
-#ifndef GSL_MSVC_HAS_TYPE_DEDUCTION_BUG
 
 template <typename CharT, std::ptrdiff_t Extent>
 std::basic_string<typename std::remove_const<CharT>::type>
 to_string(basic_string_span<CharT, Extent> view)
 {
-    return {view.data(), static_cast<size_t>(view.length())};
+    return {view.data(), static_cast<std::size_t>(view.length())};
 }
 
-#else
-
-inline std::string to_string(cstring_span<> view)
-{
-    return {view.data(), static_cast<size_t>(view.length())};
-}
-
-inline std::string to_string(string_span<> view)
-{
-    return {view.data(), static_cast<size_t>(view.length())};
-}
-
-inline std::wstring to_string(cwstring_span<> view)
-{
-    return {view.data(), static_cast<size_t>(view.length())};
-}
-
-inline std::wstring to_string(wstring_span<> view)
-{
-    return {view.data(), static_cast<size_t>(view.length())};
-}
-
-#endif
-
-template <typename CharT,
-	  typename Traits = typename std::char_traits<CharT>,
-	  typename Allocator = std::allocator<CharT>,
-	  typename gCharT,
-	  std::ptrdiff_t Extent>
+template <typename CharT, typename Traits = typename std::char_traits<CharT>,
+          typename Allocator = std::allocator<CharT>, typename gCharT, std::ptrdiff_t Extent>
 std::basic_string<CharT, Traits, Allocator> to_basic_string(basic_string_span<gCharT, Extent> view)
 {
-  return {view.data(), static_cast<size_t>(view.length())};
+    return {view.data(), static_cast<std::size_t>(view.length())};
 }
-  
+
 // zero-terminated string span, used to convert
 // zero-terminated spans to legacy strings
 template <typename CharT, std::ptrdiff_t Extent = dynamic_extent>
@@ -520,7 +459,7 @@
     using impl_type = span<value_type, Extent>;
     using string_span_type = basic_string_span<value_type, Extent>;
 
-    constexpr basic_zstring_span(impl_type s) noexcept : span_(s)
+    constexpr basic_zstring_span(impl_type s) GSL_NOEXCEPT : span_(s)
     {
         // expects a zero-terminated span
         Expects(s[s.size() - 1] == '\0');
@@ -530,37 +469,25 @@
     constexpr basic_zstring_span(const basic_zstring_span& other) = default;
 
 // move
-#ifndef GSL_MSVC_NO_DEFAULT_MOVE_CTOR
     constexpr basic_zstring_span(basic_zstring_span&& other) = default;
-#else
-    constexpr basic_zstring_span(basic_zstring_span&& other) : span_(std::move(other.span_)) {}
-#endif
 
     // assign
     constexpr basic_zstring_span& operator=(const basic_zstring_span& other) = default;
 
 // move assign
-#ifndef GSL_MSVC_NO_DEFAULT_MOVE_CTOR
     constexpr basic_zstring_span& operator=(basic_zstring_span&& other) = default;
-#else
-    constexpr basic_zstring_span& operator=(basic_zstring_span&& other)
-    {
-        span_ = std::move(other.span_);
-        return *this;
-    }
-#endif
 
-    constexpr bool empty() const noexcept { return span_.size() == 0; }
+    constexpr bool empty() const GSL_NOEXCEPT { return span_.size() == 0; }
 
-    constexpr string_span_type as_string_span() const noexcept
+    constexpr string_span_type as_string_span() const GSL_NOEXCEPT
     {
         auto sz = span_.size();
         return span_.first(sz <= 0 ? 0 : sz - 1);
     }
 
-    constexpr string_span_type ensure_z() const noexcept { return gsl::ensure_z(span_); }
+    constexpr string_span_type ensure_z() const GSL_NOEXCEPT { return gsl::ensure_z(span_); }
 
-    constexpr const_zstring_type assume_z() const noexcept { return span_.data(); }
+    constexpr const_zstring_type assume_z() const GSL_NOEXCEPT { return span_.data(); }
 
 private:
     impl_type span_;
@@ -583,35 +510,27 @@
           class = std::enable_if_t<
               details::is_basic_string_span<T>::value ||
               std::is_convertible<T, gsl::basic_string_span<std::add_const_t<CharT>>>::value>>
-bool operator==(const gsl::basic_string_span<CharT, Extent>& one, const T& other) noexcept
+bool operator==(const gsl::basic_string_span<CharT, Extent>& one, const T& other) GSL_NOEXCEPT
 {
-    gsl::basic_string_span<std::add_const_t<CharT>> tmp(other);
-#ifdef GSL_MSVC_NO_CPP14_STD_EQUAL
-    return (one.size() == tmp.size()) && std::equal(one.begin(), one.end(), tmp.begin());
-#else
+    const gsl::basic_string_span<std::add_const_t<CharT>> tmp(other);
     return std::equal(one.begin(), one.end(), tmp.begin(), tmp.end());
-#endif
 }
 
 template <class CharT, std::ptrdiff_t Extent, class T,
           class = std::enable_if_t<
               !details::is_basic_string_span<T>::value &&
               std::is_convertible<T, gsl::basic_string_span<std::add_const_t<CharT>>>::value>>
-bool operator==(const T& one, const gsl::basic_string_span<CharT, Extent>& other) noexcept
+bool operator==(const T& one, const gsl::basic_string_span<CharT, Extent>& other) GSL_NOEXCEPT
 {
     gsl::basic_string_span<std::add_const_t<CharT>> tmp(one);
-#ifdef GSL_MSVC_NO_CPP14_STD_EQUAL
-    return (tmp.size() == other.size()) && std::equal(tmp.begin(), tmp.end(), other.begin());
-#else
     return std::equal(tmp.begin(), tmp.end(), other.begin(), other.end());
-#endif
 }
 
 // operator !=
 template <typename CharT, std::ptrdiff_t Extent = gsl::dynamic_extent, typename T,
           typename = std::enable_if_t<std::is_convertible<
               T, gsl::basic_string_span<std::add_const_t<CharT>, Extent>>::value>>
-bool operator!=(gsl::basic_string_span<CharT, Extent> one, const T& other) noexcept
+bool operator!=(gsl::basic_string_span<CharT, Extent> one, const T& other) GSL_NOEXCEPT
 {
     return !(one == other);
 }
@@ -621,7 +540,7 @@
     typename Dummy = std::enable_if_t<
         std::is_convertible<T, gsl::basic_string_span<std::add_const_t<CharT>, Extent>>::value &&
         !gsl::details::is_basic_string_span<T>::value>>
-bool operator!=(const T& one, gsl::basic_string_span<CharT, Extent> other) noexcept
+bool operator!=(const T& one, gsl::basic_string_span<CharT, Extent> other) GSL_NOEXCEPT
 {
     return !(one == other);
 }
@@ -630,9 +549,9 @@
 template <typename CharT, std::ptrdiff_t Extent = gsl::dynamic_extent, typename T,
           typename = std::enable_if_t<std::is_convertible<
               T, gsl::basic_string_span<std::add_const_t<CharT>, Extent>>::value>>
-bool operator<(gsl::basic_string_span<CharT, Extent> one, const T& other) noexcept
+bool operator<(gsl::basic_string_span<CharT, Extent> one, const T& other) GSL_NOEXCEPT
 {
-    gsl::basic_string_span<std::add_const_t<CharT>, Extent> tmp(other);
+    const gsl::basic_string_span<std::add_const_t<CharT>, Extent> tmp(other);
     return std::lexicographical_compare(one.begin(), one.end(), tmp.begin(), tmp.end());
 }
 
@@ -641,7 +560,7 @@
     typename Dummy = std::enable_if_t<
         std::is_convertible<T, gsl::basic_string_span<std::add_const_t<CharT>, Extent>>::value &&
         !gsl::details::is_basic_string_span<T>::value>>
-bool operator<(const T& one, gsl::basic_string_span<CharT, Extent> other) noexcept
+bool operator<(const T& one, gsl::basic_string_span<CharT, Extent> other) GSL_NOEXCEPT
 {
     gsl::basic_string_span<std::add_const_t<CharT>, Extent> tmp(one);
     return std::lexicographical_compare(tmp.begin(), tmp.end(), other.begin(), other.end());
@@ -660,7 +579,7 @@
         std::is_convertible<DataType*, CharT*>::value &&
         std::is_same<std::decay_t<decltype(std::declval<T>().size(), *std::declval<T>().data())>,
                      DataType>::value>>
-bool operator<(gsl::basic_string_span<CharT, Extent> one, const T& other) noexcept
+bool operator<(gsl::basic_string_span<CharT, Extent> one, const T& other) GSL_NOEXCEPT
 {
     gsl::basic_string_span<std::add_const_t<CharT>, Extent> tmp(other);
     return std::lexicographical_compare(one.begin(), one.end(), tmp.begin(), tmp.end());
@@ -674,7 +593,7 @@
         std::is_convertible<DataType*, CharT*>::value &&
         std::is_same<std::decay_t<decltype(std::declval<T>().size(), *std::declval<T>().data())>,
                      DataType>::value>>
-bool operator<(const T& one, gsl::basic_string_span<CharT, Extent> other) noexcept
+bool operator<(const T& one, gsl::basic_string_span<CharT, Extent> other) GSL_NOEXCEPT
 {
     gsl::basic_string_span<std::add_const_t<CharT>, Extent> tmp(one);
     return std::lexicographical_compare(tmp.begin(), tmp.end(), other.begin(), other.end());
@@ -685,7 +604,7 @@
 template <typename CharT, std::ptrdiff_t Extent = gsl::dynamic_extent, typename T,
           typename = std::enable_if_t<std::is_convertible<
               T, gsl::basic_string_span<std::add_const_t<CharT>, Extent>>::value>>
-bool operator<=(gsl::basic_string_span<CharT, Extent> one, const T& other) noexcept
+bool operator<=(gsl::basic_string_span<CharT, Extent> one, const T& other) GSL_NOEXCEPT
 {
     return !(other < one);
 }
@@ -695,7 +614,7 @@
     typename Dummy = std::enable_if_t<
         std::is_convertible<T, gsl::basic_string_span<std::add_const_t<CharT>, Extent>>::value &&
         !gsl::details::is_basic_string_span<T>::value>>
-bool operator<=(const T& one, gsl::basic_string_span<CharT, Extent> other) noexcept
+bool operator<=(const T& one, gsl::basic_string_span<CharT, Extent> other) GSL_NOEXCEPT
 {
     return !(other < one);
 }
@@ -713,7 +632,7 @@
         std::is_convertible<DataType*, CharT*>::value &&
         std::is_same<std::decay_t<decltype(std::declval<T>().size(), *std::declval<T>().data())>,
                      DataType>::value>>
-bool operator<=(gsl::basic_string_span<CharT, Extent> one, const T& other) noexcept
+bool operator<=(gsl::basic_string_span<CharT, Extent> one, const T& other) GSL_NOEXCEPT
 {
     return !(other < one);
 }
@@ -726,7 +645,7 @@
         std::is_convertible<DataType*, CharT*>::value &&
         std::is_same<std::decay_t<decltype(std::declval<T>().size(), *std::declval<T>().data())>,
                      DataType>::value>>
-bool operator<=(const T& one, gsl::basic_string_span<CharT, Extent> other) noexcept
+bool operator<=(const T& one, gsl::basic_string_span<CharT, Extent> other) GSL_NOEXCEPT
 {
     return !(other < one);
 }
@@ -736,7 +655,7 @@
 template <typename CharT, std::ptrdiff_t Extent = gsl::dynamic_extent, typename T,
           typename = std::enable_if_t<std::is_convertible<
               T, gsl::basic_string_span<std::add_const_t<CharT>, Extent>>::value>>
-bool operator>(gsl::basic_string_span<CharT, Extent> one, const T& other) noexcept
+bool operator>(gsl::basic_string_span<CharT, Extent> one, const T& other) GSL_NOEXCEPT
 {
     return other < one;
 }
@@ -746,7 +665,7 @@
     typename Dummy = std::enable_if_t<
         std::is_convertible<T, gsl::basic_string_span<std::add_const_t<CharT>, Extent>>::value &&
         !gsl::details::is_basic_string_span<T>::value>>
-bool operator>(const T& one, gsl::basic_string_span<CharT, Extent> other) noexcept
+bool operator>(const T& one, gsl::basic_string_span<CharT, Extent> other) GSL_NOEXCEPT
 {
     return other < one;
 }
@@ -764,7 +683,7 @@
         std::is_convertible<DataType*, CharT*>::value &&
         std::is_same<std::decay_t<decltype(std::declval<T>().size(), *std::declval<T>().data())>,
                      DataType>::value>>
-bool operator>(gsl::basic_string_span<CharT, Extent> one, const T& other) noexcept
+bool operator>(gsl::basic_string_span<CharT, Extent> one, const T& other) GSL_NOEXCEPT
 {
     return other < one;
 }
@@ -777,7 +696,7 @@
         std::is_convertible<DataType*, CharT*>::value &&
         std::is_same<std::decay_t<decltype(std::declval<T>().size(), *std::declval<T>().data())>,
                      DataType>::value>>
-bool operator>(const T& one, gsl::basic_string_span<CharT, Extent> other) noexcept
+bool operator>(const T& one, gsl::basic_string_span<CharT, Extent> other) GSL_NOEXCEPT
 {
     return other < one;
 }
@@ -787,7 +706,7 @@
 template <typename CharT, std::ptrdiff_t Extent = gsl::dynamic_extent, typename T,
           typename = std::enable_if_t<std::is_convertible<
               T, gsl::basic_string_span<std::add_const_t<CharT>, Extent>>::value>>
-bool operator>=(gsl::basic_string_span<CharT, Extent> one, const T& other) noexcept
+bool operator>=(gsl::basic_string_span<CharT, Extent> one, const T& other) GSL_NOEXCEPT
 {
     return !(one < other);
 }
@@ -797,7 +716,7 @@
     typename Dummy = std::enable_if_t<
         std::is_convertible<T, gsl::basic_string_span<std::add_const_t<CharT>, Extent>>::value &&
         !gsl::details::is_basic_string_span<T>::value>>
-bool operator>=(const T& one, gsl::basic_string_span<CharT, Extent> other) noexcept
+bool operator>=(const T& one, gsl::basic_string_span<CharT, Extent> other) GSL_NOEXCEPT
 {
     return !(one < other);
 }
@@ -815,7 +734,7 @@
         std::is_convertible<DataType*, CharT*>::value &&
         std::is_same<std::decay_t<decltype(std::declval<T>().size(), *std::declval<T>().data())>,
                      DataType>::value>>
-bool operator>=(gsl::basic_string_span<CharT, Extent> one, const T& other) noexcept
+bool operator>=(gsl::basic_string_span<CharT, Extent> one, const T& other) GSL_NOEXCEPT
 {
     return !(one < other);
 }
@@ -828,43 +747,23 @@
         std::is_convertible<DataType*, CharT*>::value &&
         std::is_same<std::decay_t<decltype(std::declval<T>().size(), *std::declval<T>().data())>,
                      DataType>::value>>
-bool operator>=(const T& one, gsl::basic_string_span<CharT, Extent> other) noexcept
+bool operator>=(const T& one, gsl::basic_string_span<CharT, Extent> other) GSL_NOEXCEPT
 {
     return !(one < other);
 }
 #endif
 } // namespace GSL
 
-#ifdef _MSC_VER
+#undef GSL_NOEXCEPT
 
+#ifdef _MSC_VER
 #pragma warning(pop)
 
+#if _MSC_VER < 1910
 #undef constexpr
 #pragma pop_macro("constexpr")
 
-// VS 2013 workarounds
-#if _MSC_VER <= 1800
-
-#ifndef GSL_THROW_ON_CONTRACT_VIOLATION
-#undef noexcept
-#pragma pop_macro("noexcept")
-#endif // GSL_THROW_ON_CONTRACT_VIOLATION
-
-#undef GSL_MSVC_HAS_TYPE_DEDUCTION_BUG
-#undef GSL_MSVC_HAS_SFINAE_SUBSTITUTION_ICE
-#undef GSL_MSVC_NO_CPP14_STD_EQUAL
-#undef GSL_MSVC_NO_DEFAULT_MOVE_CTOR
-
-#endif // _MSC_VER <= 1800
+#endif // _MSC_VER < 1910
 #endif // _MSC_VER
 
-#if defined(GSL_THROW_ON_CONTRACT_VIOLATION)
-
-#undef noexcept
-
-#ifdef _MSC_VER
-#pragma pop_macro("noexcept")
-#endif
-
-#endif // GSL_THROW_ON_CONTRACT_VIOLATION
 #endif // GSL_STRING_SPAN_H
diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt
index 44db32d..5306e85 100644
--- a/tests/CMakeLists.txt
+++ b/tests/CMakeLists.txt
@@ -2,43 +2,81 @@
 
 project(GSLTests CXX)
 
-if (NOT EXISTS ${CMAKE_CURRENT_SOURCE_DIR}/unittest-cpp/tests)
-    execute_process(COMMAND git submodule update --init WORKING_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}")
-endif()
+# will make visual studio generated project group files
+set_property(GLOBAL PROPERTY USE_FOLDERS ON)
 
-add_subdirectory(unittest-cpp)
-
-include_directories(
-    ..
-    ./unittest-cpp
+list(APPEND CATCH_CMAKE_ARGS
+    "-DCMAKE_INSTALL_PREFIX=${CMAKE_SOURCE_DIR}"
+    "-DNO_SELFTEST=true"
 )
 
-add_definitions(-DGSL_THROW_ON_CONTRACT_VIOLATION)
+# add catch
+ExternalProject_Add(
+    catch
+    PREFIX ${CMAKE_BINARY_DIR}/catch
+    GIT_REPOSITORY https://github.com/philsquared/Catch.git
+    GIT_TAG v1.9.6    
+    CMAKE_ARGS ${CATCH_CMAKE_ARGS}
+    LOG_DOWNLOAD 1
+    UPDATE_DISCONNECTED 1
+)
 
-if(MSVC14 OR MSVC12) # has the support we need
-    # remove unnecessary warnings about unchecked iterators
-    add_definitions(-D_SCL_SECURE_NO_WARNINGS)
-    add_compile_options(/W4)
-else()
-    include(CheckCXXCompilerFlag)
-    CHECK_CXX_COMPILER_FLAG("-std=c++14" COMPILER_SUPPORTS_CXX14)
-    CHECK_CXX_COMPILER_FLAG("-std=c++11" COMPILER_SUPPORTS_CXX11)
-    if(COMPILER_SUPPORTS_CXX14)
-        set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fno-strict-aliasing -std=c++14 -O3 -Wall -Wno-missing-braces")
-    elseif(COMPILER_SUPPORTS_CXX11)
-        set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fno-strict-aliasing -std=c++11 -O3 -Wall -Wno-missing-braces")
-    else()
-      message(STATUS "The compiler ${CMAKE_CXX_COMPILER} has no C++11 support. Please use a different C++ compiler.")
-    endif()
-endif()
+# this interface adds compile options to how the tests are run
+# please try to keep entries ordered =)
+add_library(gsl_tests_config INTERFACE)
+target_compile_options(gsl_tests_config INTERFACE
+    $<$<CXX_COMPILER_ID:MSVC>:
+        /EHsc
+        /W4
+        /WX
+    >
+    $<$<NOT:$<CXX_COMPILER_ID:MSVC>>:
+        -fno-strict-aliasing
+        -Wall
+        -Wcast-align
+        -Wconversion
+        -Wctor-dtor-privacy
+        -Werror
+        -Wextra
+        -Wno-missing-braces
+        -Wnon-virtual-dtor
+        -Wold-style-cast
+        -Woverloaded-virtual
+        -Wpedantic
+        -Wshadow
+        -Wsign-conversion
+    >
+)
+
+# set definitions for tests
+target_compile_definitions(gsl_tests_config INTERFACE
+    GSL_THROW_ON_CONTRACT_VIOLATION
+)
+
+# create the main executable for each test. this reduces the compile time
+# of each test by pre-compiling catch.
+add_library(test_catch STATIC test.cpp)
+target_link_libraries(test_catch
+    GSL
+    gsl_tests_config
+)
+add_dependencies(test_catch catch)
+set_property(TARGET test_catch PROPERTY FOLDER "GSL_tests")
 
 function(add_gsl_test name)
-    add_executable(${name} ${name}.cpp ../gsl/gsl ../gsl/gsl_assert ../gsl/gsl_util ../gsl/multi_span ../gsl/span ../gsl/string_span)
-    target_link_libraries(${name} UnitTest++)
+    add_executable(${name} ${name}.cpp)
+    target_link_libraries(${name}
+        GSL
+        test_catch
+        gsl_tests_config
+    )
+    add_dependencies(${name} catch)
     add_test(
       ${name}
       ${name}
     )
+    # group all tests under GSL_tests
+    set_property(TARGET ${name} PROPERTY FOLDER "GSL_tests")
 endfunction()
 
 add_gsl_test(span_tests)
@@ -52,3 +90,4 @@
 add_gsl_test(utils_tests)
 add_gsl_test(owner_tests)
 add_gsl_test(byte_tests)
+add_gsl_test(algorithm_tests)
diff --git a/tests/algorithm_tests.cpp b/tests/algorithm_tests.cpp
new file mode 100644
index 0000000..045fd3e
--- /dev/null
+++ b/tests/algorithm_tests.cpp
@@ -0,0 +1,204 @@
+///////////////////////////////////////////////////////////////////////////////
+//
+// Copyright (c) 2015 Microsoft Corporation. All rights reserved.
+//
+// This code is licensed under the MIT License (MIT).
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+// THE SOFTWARE.
+//
+///////////////////////////////////////////////////////////////////////////////
+
+#include <catch/catch.hpp>
+
+#include <gsl/gsl_algorithm>
+
+#include <array>
+
+using namespace std;
+using namespace gsl;
+
+TEST_CASE("same_type")
+{
+    // dynamic source and destination span
+    {
+        std::array<int, 5> src{1, 2, 3, 4, 5};
+        std::array<int, 10> dst{};
+
+        span<int> src_span(src);
+        span<int> dst_span(dst);
+
+        copy(src_span, dst_span);
+        copy(src_span, dst_span.subspan(src_span.size()));
+
+        for (std::size_t i = 0; i < src.size(); ++i) {
+            CHECK(dst[i] == src[i]);
+            CHECK(dst[i + src.size()] == src[i]);
+        }
+    }
+
+    // static source and dynamic destination span
+    {
+        std::array<int, 5> src{1, 2, 3, 4, 5};
+        std::array<int, 10> dst{};
+
+        span<int, 5> src_span(src);
+        span<int> dst_span(dst);
+
+        copy(src_span, dst_span);
+        copy(src_span, dst_span.subspan(src_span.size()));
+
+        for (std::size_t i = 0; i < src.size(); ++i) {
+            CHECK(dst[i] == src[i]);
+            CHECK(dst[i + src.size()] == src[i]);
+        }
+    }
+
+    // dynamic source and static destination span
+    {
+        std::array<int, 5> src{1, 2, 3, 4, 5};
+        std::array<int, 10> dst{};
+
+        span<int> src_span(src);
+        span<int, 10> dst_span(dst);
+
+        copy(src_span, dst_span);
+        copy(src_span, dst_span.subspan(src_span.size()));
+
+        for (std::size_t i = 0; i < src.size(); ++i) {
+            CHECK(dst[i] == src[i]);
+            CHECK(dst[i + src.size()] == src[i]);
+        }
+    }
+
+    // static source and destination span
+    {
+        std::array<int, 5> src{1, 2, 3, 4, 5};
+        std::array<int, 10> dst{};
+
+        span<int, 5> src_span(src);
+        span<int, 10> dst_span(dst);
+
+        copy(src_span, dst_span);
+        copy(src_span, dst_span.subspan(src_span.size()));
+
+        for (std::size_t i = 0; i < src.size(); ++i) {
+            CHECK(dst[i] == src[i]);
+            CHECK(dst[i + src.size()] == src[i]);
+        }
+    }
+}
+
+TEST_CASE("compatible_type")
+{
+    // dynamic source and destination span
+    {
+        std::array<short, 5> src{1, 2, 3, 4, 5};
+        std::array<int, 10> dst{};
+
+        span<short> src_span(src);
+        span<int> dst_span(dst);
+
+        copy(src_span, dst_span);
+        copy(src_span, dst_span.subspan(src_span.size()));
+
+        for (std::size_t i = 0; i < src.size(); ++i) {
+            CHECK(dst[i] == src[i]);
+            CHECK(dst[i + src.size()] == src[i]);
+        }
+    }
+
+    // static source and dynamic destination span
+    {
+        std::array<short, 5> src{1, 2, 3, 4, 5};
+        std::array<int, 10> dst{};
+
+        span<short, 5> src_span(src);
+        span<int> dst_span(dst);
+
+        copy(src_span, dst_span);
+        copy(src_span, dst_span.subspan(src_span.size()));
+
+        for (std::size_t i = 0; i < src.size(); ++i) {
+            CHECK(dst[i] == src[i]);
+            CHECK(dst[i + src.size()] == src[i]);
+        }
+    }
+
+    // dynamic source and static destination span
+    {
+        std::array<short, 5> src{1, 2, 3, 4, 5};
+        std::array<int, 10> dst{};
+
+        span<short> src_span(src);
+        span<int, 10> dst_span(dst);
+
+        copy(src_span, dst_span);
+        copy(src_span, dst_span.subspan(src_span.size()));
+
+        for (std::size_t i = 0; i < src.size(); ++i) {
+            CHECK(dst[i] == src[i]);
+            CHECK(dst[i + src.size()] == src[i]);
+        }
+    }
+
+    // static source and destination span
+    {
+        std::array<short, 5> src{1, 2, 3, 4, 5};
+        std::array<int, 10> dst{};
+
+        span<short, 5> src_span(src);
+        span<int, 10> dst_span(dst);
+
+        copy(src_span, dst_span);
+        copy(src_span, dst_span.subspan(src_span.size()));
+
+        for (std::size_t i = 0; i < src.size(); ++i) {
+            CHECK(dst[i] == src[i]);
+            CHECK(dst[i + src.size()] == src[i]);
+        }
+    }
+}
+
+#ifdef CONFIRM_COMPILATION_ERRORS
+TEST_CASE("incompatible_type")
+{
+    std::array<int, 4> src{1, 2, 3, 4};
+    std::array<int*, 12> dst{};
+
+    span<int> src_span_dyn(src);
+    span<int, 4> src_span_static(src);
+    span<int*> dst_span_dyn(dst);
+    span<int*, 4> dst_span_static(dst);
+
+    // every line should produce a compilation error
+    copy(src_span_dyn, dst_span_dyn);
+    copy(src_span_dyn, dst_span_static);
+    copy(src_span_static, dst_span_dyn);
+    copy(src_span_static, dst_span_static);
+}
+#endif
+
+TEST_CASE("small_destination_span")
+{
+    std::array<int, 12> src{1, 2, 3, 4};
+    std::array<int, 4> dst{};
+
+    span<int> src_span_dyn(src);
+    span<int, 12> src_span_static(src);
+    span<int> dst_span_dyn(dst);
+    span<int, 4> dst_span_static(dst);
+
+    CHECK_THROWS_AS(copy(src_span_dyn, dst_span_dyn), fail_fast);
+    CHECK_THROWS_AS(copy(src_span_dyn, dst_span_static), fail_fast);
+    CHECK_THROWS_AS(copy(src_span_static, dst_span_dyn), fail_fast);
+
+#ifdef CONFIRM_COMPILATION_ERRORS
+    copy(src_span_static, dst_span_static);
+#endif
+}
diff --git a/tests/assertion_tests.cpp b/tests/assertion_tests.cpp
index a251200..42966d1 100644
--- a/tests/assertion_tests.cpp
+++ b/tests/assertion_tests.cpp
@@ -1,53 +1,46 @@
-/////////////////////////////////////////////////////////////////////////////// 
-// 
-// Copyright (c) 2015 Microsoft Corporation. All rights reserved. 
-// 
-// This code is licensed under the MIT License (MIT). 
-// 
-// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 
-// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 
-// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 
-// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 
-// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 
-// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 
-// THE SOFTWARE. 
-// 
+///////////////////////////////////////////////////////////////////////////////
+//
+// Copyright (c) 2015 Microsoft Corporation. All rights reserved.
+//
+// This code is licensed under the MIT License (MIT).
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+// THE SOFTWARE.
+//
 ///////////////////////////////////////////////////////////////////////////////
 
-#include <UnitTest++/UnitTest++.h> 
+#include <catch/catch.hpp>
+
 #include <gsl/gsl>
 
 using namespace gsl;
 
-SUITE(assertion_tests)
+int f(int i)
 {
-    int f(int i)
-    {
-        Expects(i > 0 && i < 10);
-        return i;
-    }
-
-    TEST(expects)
-    {
-        CHECK(f(2) == 2);
-        CHECK_THROW(f(10), fail_fast);
-    }
-
-    int g(int i)
-    {        
-        i++;
-        Ensures(i > 0 && i < 10);
-        return i;
-    }
-
-    TEST(ensures)
-    {
-        CHECK(g(2) == 3);
-        CHECK_THROW(g(9), fail_fast);
-    }
+    Expects(i > 0 && i < 10);
+    return i;
 }
 
-int main(int, const char *[])
+TEST_CASE("expects")
 {
-    return UnitTest::RunAllTests();
+    CHECK(f(2) == 2);
+    CHECK_THROWS_AS(f(10), fail_fast);
+}
+
+int g(int i)
+{
+    i++;
+    Ensures(i > 0 && i < 10);
+    return i;
+}
+
+TEST_CASE("ensures")
+{
+    CHECK(g(2) == 3);
+    CHECK_THROWS_AS(g(9), fail_fast);
 }
diff --git a/tests/at_tests.cpp b/tests/at_tests.cpp
index 008fddf..78e8e3d 100644
--- a/tests/at_tests.cpp
+++ b/tests/at_tests.cpp
@@ -1,71 +1,110 @@
-/////////////////////////////////////////////////////////////////////////////// 
-// 
-// Copyright (c) 2015 Microsoft Corporation. All rights reserved. 
-// 
-// This code is licensed under the MIT License (MIT). 
-// 
-// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 
-// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 
-// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 
-// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 
-// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 
-// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 
-// THE SOFTWARE. 
-// 
+///////////////////////////////////////////////////////////////////////////////
+//
+// Copyright (c) 2015 Microsoft Corporation. All rights reserved.
+//
+// This code is licensed under the MIT License (MIT).
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+// THE SOFTWARE.
+//
 ///////////////////////////////////////////////////////////////////////////////
 
-#include <UnitTest++/UnitTest++.h> 
+#include <catch/catch.hpp>
+
 #include <gsl/gsl>
-#include <vector>
+
 #include <initializer_list>
+#include <vector>
 
-using namespace std;
-using namespace gsl;
+using gsl::fail_fast;
 
-SUITE(at_tests)
+TEST_CASE("static_array")
 {
-    TEST(static_array)
-    {
-        int a[] = { 1, 2, 3, 4 };
+    int a[4] = {1, 2, 3, 4};
+    const int(&c_a)[4] = a;
 
-        for (int i = 0; i < 4; ++i)
-            CHECK(at(a, i) == i+1);
-
-        CHECK_THROW(at(a, 4), fail_fast);
+    for (int i = 0; i < 4; ++i) {
+        CHECK(&gsl::at(a, i) == &a[i]);
+        CHECK(&gsl::at(c_a, i) == &a[i]);
     }
 
-    TEST(std_array)
-    {
-        std::array<int,4> a = { 1, 2, 3, 4 };
-
-        for (int i = 0; i < 4; ++i)
-            CHECK(at(a, i) == i+1);
-
-        CHECK_THROW(at(a, 4), fail_fast);
-    }
-
-    TEST(StdVector)
-    {
-        std::vector<int> a = { 1, 2, 3, 4 };
-
-        for (int i = 0; i < 4; ++i)
-            CHECK(at(a, i) == i+1);
-
-        CHECK_THROW(at(a, 4), fail_fast);
-    }
-
-    TEST(InitializerList)
-    {
-        std::initializer_list<int> a = { 1, 2, 3, 4 };
-
-        for (int i = 0; i < 4; ++i)
-            CHECK(at(a, i) == i+1);
-
-        CHECK_THROW(at(a, 4), fail_fast);
-    }
+    CHECK_THROWS_AS(gsl::at(a, -1), fail_fast);
+    CHECK_THROWS_AS(gsl::at(a, 4), fail_fast);
+    CHECK_THROWS_AS(gsl::at(c_a, -1), fail_fast);
+    CHECK_THROWS_AS(gsl::at(c_a, 4), fail_fast);
 }
 
-int main(int, const char *[])
+TEST_CASE("std_array")
 {
-    return UnitTest::RunAllTests();
+    std::array<int, 4> a = {1, 2, 3, 4};
+    const std::array<int, 4>& c_a = a;
+
+    for (int i = 0; i < 4; ++i) {
+        CHECK(&gsl::at(a, i) == &a[static_cast<std::size_t>(i)]);
+        CHECK(&gsl::at(c_a, i) == &a[static_cast<std::size_t>(i)]);
+    }
+
+    CHECK_THROWS_AS(gsl::at(a, -1), fail_fast);
+    CHECK_THROWS_AS(gsl::at(a, 4), fail_fast);
+    CHECK_THROWS_AS(gsl::at(c_a, -1), fail_fast);
+    CHECK_THROWS_AS(gsl::at(c_a, 4), fail_fast);
 }
+
+TEST_CASE("StdVector")
+{
+    std::vector<int> a = {1, 2, 3, 4};
+    const std::vector<int>& c_a = a;
+
+    for (int i = 0; i < 4; ++i) {
+        CHECK(&gsl::at(a, i) == &a[static_cast<std::size_t>(i)]);
+        CHECK(&gsl::at(c_a, i) == &a[static_cast<std::size_t>(i)]);
+    }
+
+    CHECK_THROWS_AS(gsl::at(a, -1), fail_fast);
+    CHECK_THROWS_AS(gsl::at(a, 4), fail_fast);
+    CHECK_THROWS_AS(gsl::at(c_a, -1), fail_fast);
+    CHECK_THROWS_AS(gsl::at(c_a, 4), fail_fast);
+}
+
+TEST_CASE("InitializerList")
+{
+    std::initializer_list<int> a = {1, 2, 3, 4};
+
+    for (int i = 0; i < 4; ++i) {
+        CHECK(gsl::at(a, i) == i + 1);
+        CHECK(gsl::at({1, 2, 3, 4}, i) == i + 1);
+    }
+
+    CHECK_THROWS_AS(gsl::at(a, -1), fail_fast);
+    CHECK_THROWS_AS(gsl::at(a, 4), fail_fast);
+    CHECK_THROWS_AS(gsl::at({1, 2, 3, 4}, -1), fail_fast);
+    CHECK_THROWS_AS(gsl::at({1, 2, 3, 4}, 4), fail_fast);
+}
+
+#if !defined(_MSC_VER) || defined(__clang__) || _MSC_VER >= 1910
+static constexpr bool test_constexpr()
+{
+    int a1[4] = {1, 2, 3, 4};
+    const int(&c_a1)[4] = a1;
+    std::array<int, 4> a2 = {1, 2, 3, 4};
+    const std::array<int, 4>& c_a2 = a2;
+
+    for (int i = 0; i < 4; ++i) {
+        if (&gsl::at(a1, i) != &a1[i]) return false;
+        if (&gsl::at(c_a1, i) != &a1[i]) return false;
+        // requires C++17:
+        // if (&gsl::at(a2, i) != &a2[static_cast<std::size_t>(i)]) return false;
+        if (&gsl::at(c_a2, i) != &c_a2[static_cast<std::size_t>(i)]) return false;
+        if (gsl::at({1, 2, 3, 4}, i) != i + 1) return false;
+    }
+
+    return true;
+}
+
+static_assert(test_constexpr(), "FAIL");
+#endif
diff --git a/tests/bounds_tests.cpp b/tests/bounds_tests.cpp
index d10bf6d..51b5393 100644
--- a/tests/bounds_tests.cpp
+++ b/tests/bounds_tests.cpp
@@ -1,103 +1,95 @@
-/////////////////////////////////////////////////////////////////////////////// 
-// 
-// Copyright (c) 2015 Microsoft Corporation. All rights reserved. 
-// 
-// This code is licensed under the MIT License (MIT). 
-// 
-// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 
-// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 
-// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 
-// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 
-// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 
-// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 
-// THE SOFTWARE. 
-// 
+///////////////////////////////////////////////////////////////////////////////
+//
+// Copyright (c) 2015 Microsoft Corporation. All rights reserved.
+//
+// This code is licensed under the MIT License (MIT).
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+// THE SOFTWARE.
+//
 ///////////////////////////////////////////////////////////////////////////////
 
-#include <UnitTest++/UnitTest++.h> 
+#include <catch/catch.hpp>
+
 #include <gsl/multi_span>
+
 #include <vector>
 
 using namespace std;
-using namespace gsl;;
+using namespace gsl;
 
-namespace 
+namespace
 {
-    void use(std::ptrdiff_t&) {}
+void use(std::ptrdiff_t&) {}
 }
 
-SUITE(bounds_test)
+TEST_CASE("basic_bounds")
 {
-	TEST(basic_bounds)
-	{
-		for (auto point : static_bounds<dynamic_range, 3, 4 > { 2 })
-		{
-			for (decltype(point)::size_type j = 0;
-			     j < static_cast<decltype(point)::size_type>(decltype(point)::rank);
-			     j++)
-			{
-				use(j);
-				use(point[j]);
-			}
-		}
-	}
-	
-	TEST(bounds_basic)
-	{
-		static_bounds<3, 4, 5> b;
-		auto a = b.slice();
-		(void)a;           
-		static_bounds<4, dynamic_range, 2> x{ 4 };
-		x.slice().slice();
-	}
-	
-	TEST (arrayview_iterator)
-	{
-		static_bounds<4, dynamic_range, 2> bounds{ 3 };
-		
-		auto itr = bounds.begin();
-		(void)itr;	
+    for (auto point : static_bounds<dynamic_range, 3, 4>{2}) {
+        for (decltype(point)::size_type j = 0;
+             j < static_cast<decltype(point)::size_type>(decltype(point)::rank); j++)
+        {
+            use(j);
+            use(point[static_cast<std::size_t>(j)]);
+        }
+    }
+}
+
+TEST_CASE("bounds_basic")
+{
+    static_bounds<3, 4, 5> b;
+    const auto a = b.slice();
+    (void) a;
+    static_bounds<4, dynamic_range, 2> x{4};
+    x.slice().slice();
+}
+
+TEST_CASE("arrayview_iterator")
+{
+    static_bounds<4, dynamic_range, 2> bounds{3};
+
+    const auto itr = bounds.begin();
+    (void) itr;
 #ifdef CONFIRM_COMPILATION_ERRORS
-		multi_span<int, 4, dynamic_range, 2> av(nullptr, bounds);
-	
-		auto itr2 = av.cbegin();
-	
-		for (auto& v : av) {
-			v = 4;
-		}
-		fill(av.begin(), av.end(), 0);
-#endif			
-	}
-	
-	TEST (bounds_convertible)
-	{
-		static_bounds<7, 4, 2> b1;
-		static_bounds<7, dynamic_range, 2> b2 = b1;
-		(void)b2;	
-#ifdef CONFIRM_COMPILATION_ERRORS
-		static_bounds<7, dynamic_range, 1> b4 = b2; 
+    multi_span<int, 4, dynamic_range, 2> av(nullptr, bounds);
+
+    auto itr2 = av.cbegin();
+
+    for (auto& v : av) {
+        v = 4;
+    }
+    fill(av.begin(), av.end(), 0);
 #endif
-	
-		static_bounds<dynamic_range, dynamic_range, dynamic_range> b3 = b1;
-		static_bounds<7, 4, 2> b4 = b3; 
-		(void)b4;
-
-		static_bounds<dynamic_range> b11;
-	
-		static_bounds<dynamic_range> b5;
-		static_bounds<34> b6;
-		
-		b5 = static_bounds<20>();
-		CHECK_THROW(b6 = b5, fail_fast);
-		b5 = static_bounds<34>();
-		b6 = b5;
-
-		CHECK(b5 == b6);
-		CHECK(b5.size() == b6.size());
-	}                                 
 }
 
-int main(int, const char *[])
+TEST_CASE("bounds_convertible")
 {
-	return UnitTest::RunAllTests();
+    static_bounds<7, 4, 2> b1;
+    static_bounds<7, dynamic_range, 2> b2 = b1;
+    (void) b2;
+#ifdef CONFIRM_COMPILATION_ERRORS
+    static_bounds<7, dynamic_range, 1> b4 = b2;
+#endif
+
+    static_bounds<dynamic_range, dynamic_range, dynamic_range> b3 = b1;
+    static_bounds<7, 4, 2> b4 = b3;
+    (void) b4;
+
+    static_bounds<dynamic_range> b11;
+
+    static_bounds<dynamic_range> b5;
+    static_bounds<34> b6;
+
+    b5 = static_bounds<20>();
+    CHECK_THROWS_AS(b6 = b5, fail_fast);
+    b5 = static_bounds<34>();
+    b6 = b5;
+
+    CHECK(b5 == b6);
+    CHECK(b5.size() == b6.size());
 }
diff --git a/tests/byte_tests.cpp b/tests/byte_tests.cpp
index 8cb0da8..2c6259d 100644
--- a/tests/byte_tests.cpp
+++ b/tests/byte_tests.cpp
@@ -14,7 +14,8 @@
 //
 ///////////////////////////////////////////////////////////////////////////////
 
-#include <UnitTest++/UnitTest++.h>
+#include <catch/catch.hpp>
+
 #include <gsl/gsl_byte>
 
 #include <iostream>
@@ -30,106 +31,101 @@
 namespace
 {
 
-SUITE(byte_tests)
+TEST_CASE("construction")
 {
-    TEST(construction)
     {
-        {
-            byte b = static_cast<byte>(4);
-            CHECK(static_cast<unsigned char>(b) == 4);
-        }
-
-        {
-            byte b = byte(12);
-            CHECK(static_cast<unsigned char>(b) == 12);
-        }
-
-        {
-            byte b = to_byte<12>();
-            CHECK(static_cast<unsigned char>(b) == 12);
-        }
-        {
-            unsigned char uc = 12;
-            byte b = to_byte(uc);
-            CHECK(static_cast<unsigned char>(b) == 12);
-        }
-
-        // waiting for C++17 enum class direct initializer support
-        //{
-        //    byte b { 14 };
-        //    CHECK(static_cast<unsigned char>(b) == 14);
-        //}
+        const byte b = static_cast<byte>(4);
+        CHECK(static_cast<unsigned char>(b) == 4);
     }
 
-    TEST(bitwise_operations)
     {
-        byte b = to_byte<0xFF>();
-
-        byte a = to_byte<0x00>();
-        CHECK((b | a) == to_byte<0xFF>());
-        CHECK(a == to_byte<0x00>());
-
-        a |= b;
-        CHECK(a == to_byte<0xFF>());
-
-        a = to_byte<0x01>();
-        CHECK((b & a) == to_byte<0x01>());
-
-        a &= b;
-        CHECK(a == to_byte<0x01>());
-
-        CHECK((b ^ a) == to_byte<0xFE>());
-        
-        CHECK(a == to_byte<0x01>());
-        a ^= b;
-        CHECK(a == to_byte<0xFE>());
-
-        a = to_byte<0x01>();
-        CHECK(~a == to_byte<0xFE>());
-
-        a = to_byte<0xFF>();
-        CHECK((a << 4) == to_byte<0xF0>());
-        CHECK((a >> 4) == to_byte<0x0F>());
-
-        a <<= 4;
-        CHECK(a == to_byte<0xF0>());
-        a >>= 4;
-        CHECK(a == to_byte<0x0F>());
+        const byte b = byte(12);
+        CHECK(static_cast<unsigned char>(b) == 12);
     }
 
-    TEST(to_integer)
     {
-        byte b = to_byte<0x12>();
-
-        CHECK(0x12 == gsl::to_integer<char>(b));
-        CHECK(0x12 == gsl::to_integer<short>(b));
-        CHECK(0x12 == gsl::to_integer<long>(b));
-        CHECK(0x12 == gsl::to_integer<long long>(b));
-
-        CHECK(0x12 == gsl::to_integer<unsigned char>(b));
-        CHECK(0x12 == gsl::to_integer<unsigned short>(b));
-        CHECK(0x12 == gsl::to_integer<unsigned long>(b));
-        CHECK(0x12 == gsl::to_integer<unsigned long long>(b));
-
-//      CHECK(0x12 == gsl::to_integer<float>(b));   // expect compile-time error
-//      CHECK(0x12 == gsl::to_integer<double>(b));  // expect compile-time error
+        const byte b = to_byte<12>();
+        CHECK(static_cast<unsigned char>(b) == 12);
+    }
+    {
+        const unsigned char uc = 12;
+        const byte b = to_byte(uc);
+        CHECK(static_cast<unsigned char>(b) == 12);
     }
 
-    int modify_both(gsl::byte& b, int& i)
-    {
-        i = 10;
-        b = to_byte<5>();
-        return i;
-    }
+    // waiting for C++17 enum class direct initializer support
+    //{
+    //    byte b { 14 };
+    //    CHECK(static_cast<unsigned char>(b) == 14);
+    //}
+}
 
-    TEST(aliasing)
-    {
-        int i{ 0 };
-        int res = modify_both(reinterpret_cast<byte&>(i), i);
-        CHECK(res == i);
-    }
+TEST_CASE("bitwise_operations")
+{
+    const byte b = to_byte<0xFF>();
+
+    byte a = to_byte<0x00>();
+    CHECK((b | a) == to_byte<0xFF>());
+    CHECK(a == to_byte<0x00>());
+
+    a |= b;
+    CHECK(a == to_byte<0xFF>());
+
+    a = to_byte<0x01>();
+    CHECK((b & a) == to_byte<0x01>());
+
+    a &= b;
+    CHECK(a == to_byte<0x01>());
+
+    CHECK((b ^ a) == to_byte<0xFE>());
+
+    CHECK(a == to_byte<0x01>());
+    a ^= b;
+    CHECK(a == to_byte<0xFE>());
+
+    a = to_byte<0x01>();
+    CHECK(~a == to_byte<0xFE>());
+
+    a = to_byte<0xFF>();
+    CHECK((a << 4) == to_byte<0xF0>());
+    CHECK((a >> 4) == to_byte<0x0F>());
+
+    a <<= 4;
+    CHECK(a == to_byte<0xF0>());
+    a >>= 4;
+    CHECK(a == to_byte<0x0F>());
+}
+
+TEST_CASE("to_integer")
+{
+    const byte b = to_byte<0x12>();
+
+    CHECK(0x12 == gsl::to_integer<char>(b));
+    CHECK(0x12 == gsl::to_integer<short>(b));
+    CHECK(0x12 == gsl::to_integer<long>(b));
+    CHECK(0x12 == gsl::to_integer<long long>(b));
+
+    CHECK(0x12 == gsl::to_integer<unsigned char>(b));
+    CHECK(0x12 == gsl::to_integer<unsigned short>(b));
+    CHECK(0x12 == gsl::to_integer<unsigned long>(b));
+    CHECK(0x12 == gsl::to_integer<unsigned long long>(b));
+
+    //      CHECK(0x12 == gsl::to_integer<float>(b));   // expect compile-time error
+    //      CHECK(0x12 == gsl::to_integer<double>(b));  // expect compile-time error
+}
+
+int modify_both(gsl::byte & b, int& i)
+{
+    i = 10;
+    b = to_byte<5>();
+    return i;
+}
+
+TEST_CASE("aliasing")
+{
+    int i{0};
+    const int res = modify_both(reinterpret_cast<byte&>(i), i);
+    CHECK(res == i);
 }
 
 }
-
-int main(int, const char* []) { return UnitTest::RunAllTests(); }
diff --git a/tests/multi_span_tests.cpp b/tests/multi_span_tests.cpp
index c0240ea..af58d76 100644
--- a/tests/multi_span_tests.cpp
+++ b/tests/multi_span_tests.cpp
@@ -14,7 +14,8 @@
 //
 ///////////////////////////////////////////////////////////////////////////////
 
-#include <UnitTest++/UnitTest++.h>
+#include <catch/catch.hpp>
+
 #include <gsl/multi_span>
 
 #include <iostream>
@@ -37,1649 +38,1656 @@
 };
 }
 
-SUITE(multi_span_tests)
+TEST_CASE("default_constructor")
 {
-
-    TEST(default_constructor)
     {
-        {
-            multi_span<int> s;
-            CHECK(s.length() == 0 && s.data() == nullptr);
+        multi_span<int> s;
+        CHECK((s.length() == 0 && s.data() == nullptr));
 
-            multi_span<const int> cs;
-            CHECK(cs.length() == 0 && cs.data() == nullptr);
-        }
-
-        {
-            multi_span<int, 0> s;
-            CHECK(s.length() == 0 && s.data() == nullptr);
-
-            multi_span<const int, 0> cs;
-            CHECK(cs.length() == 0 && cs.data() == nullptr);
-        }
-
-        {
-#ifdef CONFIRM_COMPILATION_ERRORS
-            multi_span<int, 1> s;
-            CHECK(s.length() == 1 && s.data() == nullptr); // explains why it can't compile
-#endif
-        }
-
-        {
-            multi_span<int> s{};
-            CHECK(s.length() == 0 && s.data() == nullptr);
-
-            multi_span<const int> cs{};
-            CHECK(cs.length() == 0 && cs.data() == nullptr);
-        }
+        multi_span<const int> cs;
+        CHECK((cs.length() == 0 && cs.data() == nullptr));
     }
 
-    TEST(from_nullptr_constructor)
     {
-        {
-            multi_span<int> s = nullptr;
-            CHECK(s.length() == 0 && s.data() == nullptr);
+        multi_span<int, 0> s;
+        CHECK((s.length() == 0 && s.data() == nullptr));
 
-            multi_span<const int> cs = nullptr;
-            CHECK(cs.length() == 0 && cs.data() == nullptr);
-        }
-
-        {
-            multi_span<int, 0> s = nullptr;
-            CHECK(s.length() == 0 && s.data() == nullptr);
-
-            multi_span<const int, 0> cs = nullptr;
-            CHECK(cs.length() == 0 && cs.data() == nullptr);
-        }
-
-        {
-#ifdef CONFIRM_COMPILATION_ERRORS
-            multi_span<int, 1> s = nullptr;
-            CHECK(s.length() == 1 && s.data() == nullptr); // explains why it can't compile
-#endif
-        }
-
-        {
-            multi_span<int> s{nullptr};
-            CHECK(s.length() == 0 && s.data() == nullptr);
-
-            multi_span<const int> cs{nullptr};
-            CHECK(cs.length() == 0 && cs.data() == nullptr);
-        }
-
-        {
-            multi_span<int*> s{nullptr};
-            CHECK(s.length() == 0 && s.data() == nullptr);
-
-            multi_span<const int*> cs{nullptr};
-            CHECK(cs.length() == 0 && cs.data() == nullptr);
-        }
+        multi_span<const int, 0> cs;
+        CHECK((cs.length() == 0 && cs.data() == nullptr));
     }
 
-    TEST(from_nullptr_length_constructor)
-    {
-        {
-            multi_span<int> s{nullptr, 0};
-            CHECK(s.length() == 0 && s.data() == nullptr);
-
-            multi_span<const int> cs{nullptr, 0};
-            CHECK(cs.length() == 0 && cs.data() == nullptr);
-        }
-
-        {
-            multi_span<int, 0> s{nullptr, 0};
-            CHECK(s.length() == 0 && s.data() == nullptr);
-
-            multi_span<const int, 0> cs{nullptr, 0};
-            CHECK(cs.length() == 0 && cs.data() == nullptr);
-        }
-
-        {
-#ifdef CONFIRM_COMPILATION_ERRORS
-            multi_span<int, 1> s{nullptr, 0};
-            CHECK(s.length() == 1 && s.data() == nullptr); // explains why it can't compile
-#endif
-        }
-
-        {
-            auto workaround_macro = []() { multi_span<int> s{nullptr, 1}; };
-            CHECK_THROW(workaround_macro(), fail_fast);
-
-            auto const_workaround_macro = []() { multi_span<const int> cs{nullptr, 1}; };
-            CHECK_THROW(const_workaround_macro(), fail_fast);
-        }
-
-        {
-            auto workaround_macro = []() { multi_span<int, 0> s{nullptr, 1}; };
-            CHECK_THROW(workaround_macro(), fail_fast);
-
-            auto const_workaround_macro = []() { multi_span<const int, 0> s{nullptr, 1}; };
-            CHECK_THROW(const_workaround_macro(), fail_fast);
-        }
-
-        {
-            multi_span<int*> s{nullptr, 0};
-            CHECK(s.length() == 0 && s.data() == nullptr);
-
-            multi_span<const int*> cs{nullptr, 0};
-            CHECK(cs.length() == 0 && cs.data() == nullptr);
-        }
-    }
-
-    TEST(from_element_constructor)
-    {
-        int i = 5;
-
-        {
-            multi_span<int> s = i;
-            CHECK(s.length() == 1 && s.data() == &i);
-            CHECK(s[0] == 5);
-
-            multi_span<const int> cs = i;
-            CHECK(cs.length() == 1 && cs.data() == &i);
-            CHECK(cs[0] == 5);
-        }
-
-        {
-#ifdef CONFIRM_COMPILATION_ERRORS
-            const j = 1;
-            multi_span<int, 0> s = j;
-#endif
-        }
-
-        {
-#ifdef CONFIRM_COMPILATION_ERRORS
-            multi_span<int, 0> s = i;
-            CHECK(s.length() == 0 && s.data() == &i);
-#endif
-        }
-
-        {
-            multi_span<int, 1> s = i;
-            CHECK(s.length() == 1 && s.data() == &i);
-            CHECK(s[0] == 5);
-        }
-
-        {
-#ifdef CONFIRM_COMPILATION_ERRORS
-            multi_span<int, 2> s = i;
-            CHECK(s.length() == 2 && s.data() == &i);
-#endif
-        }
-
-        {
-#ifdef CONFIRM_COMPILATION_ERRORS
-            auto get_a_temp = []() -> int { return 4; };
-            auto use_a_span = [](multi_span<int> s) { (void) s; };
-            use_a_span(get_a_temp());
-#endif
-        }
-    }
-
-    TEST(from_pointer_length_constructor)
-    {
-        int arr[4] = {1, 2, 3, 4};
-
-        {
-            multi_span<int> s{&arr[0], 2};
-            CHECK(s.length() == 2 && s.data() == &arr[0]);
-            CHECK(s[0] == 1 && s[1] == 2);
-        }
-
-        {
-            multi_span<int, 2> s{&arr[0], 2};
-            CHECK(s.length() == 2 && s.data() == &arr[0]);
-            CHECK(s[0] == 1 && s[1] == 2);
-        }
-
-        {
-            int* p = nullptr;
-            multi_span<int> s{p, 0};
-            CHECK(s.length() == 0 && s.data() == nullptr);
-        }
-
-        {
-            int* p = nullptr;
-            auto workaround_macro = [=]() { multi_span<int> s{p, 2}; };
-            CHECK_THROW(workaround_macro(), fail_fast);
-        }
-    }
-
-    TEST(from_pointer_pointer_constructor)
-    {
-        int arr[4] = {1, 2, 3, 4};
-
-        {
-            multi_span<int> s{&arr[0], &arr[2]};
-            CHECK(s.length() == 2 && s.data() == &arr[0]);
-            CHECK(s[0] == 1 && s[1] == 2);
-        }
-
-        {
-            multi_span<int, 2> s{&arr[0], &arr[2]};
-            CHECK(s.length() == 2 && s.data() == &arr[0]);
-            CHECK(s[0] == 1 && s[1] == 2);
-        }
-
-        {
-            multi_span<int> s{&arr[0], &arr[0]};
-            CHECK(s.length() == 0 && s.data() == &arr[0]);
-        }
-
-        {
-            multi_span<int, 0> s{&arr[0], &arr[0]};
-            CHECK(s.length() == 0 && s.data() == &arr[0]);
-        }
-
-        {
-            auto workaround_macro = [&]() { multi_span<int> s{&arr[1], &arr[0]}; };
-            CHECK_THROW(workaround_macro(), fail_fast);
-        }
-
-        {
-            int* p = nullptr;
-            auto workaround_macro = [&]() { multi_span<int> s{&arr[0], p}; };
-            CHECK_THROW(workaround_macro(), fail_fast);
-        }
-
-        {
-            int* p = nullptr;
-            auto workaround_macro = [&]() { multi_span<int> s{p, p}; };
-            CHECK_THROW(workaround_macro(), fail_fast);
-        }
-
-        {
-            int* p = nullptr;
-            auto workaround_macro = [&]() { multi_span<int> s{&arr[0], p}; };
-            CHECK_THROW(workaround_macro(), fail_fast);
-        }
-    }
-
-    TEST(from_array_constructor)
-    {
-        int arr[5] = {1, 2, 3, 4, 5};
-
-        {
-            multi_span<int> s{arr};
-            CHECK(s.length() == 5 && s.data() == &arr[0]);
-        }
-
-        {
-            multi_span<int, 5> s{arr};
-            CHECK(s.length() == 5 && s.data() == &arr[0]);
-        }
-
-        {
-#ifdef CONFIRM_COMPILATION_ERRORS
-            multi_span<int, 6> s{arr};
-#endif
-        }
-
-        {
-            multi_span<int, 0> s{arr};
-            CHECK(s.length() == 0 && s.data() == &arr[0]);
-        }
-
-        int arr2d[2][3] = {1, 2, 3, 4, 5, 6};
-
-        {
-            multi_span<int> s{arr2d};
-            CHECK(s.length() == 6 && s.data() == &arr2d[0][0]);
-            CHECK(s[0] == 1 && s[5] == 6);
-        }
-
-        {
-            multi_span<int, 0> s{arr2d};
-            CHECK(s.length() == 0 && s.data() == &arr2d[0][0]);
-        }
-
-        {
-#ifdef CONFIRM_COMPILATION_ERRORS
-            multi_span<int, 5> s{arr2d};
-#endif
-        }
-
-        {
-            multi_span<int, 6> s{arr2d};
-            CHECK(s.length() == 6 && s.data() == &arr2d[0][0]);
-            CHECK(s[0] == 1 && s[5] == 6);
-        }
-
-        {
-#ifdef CONFIRM_COMPILATION_ERRORS
-            multi_span<int, 7> s{arr2d};
-#endif
-        }
-
-        {
-            multi_span<int[3]> s{arr2d[0]};
-            CHECK(s.length() == 1 && s.data() == &arr2d[0]);
-        }
-
-        {
-            multi_span<int, 2, 3> s{arr2d};
-            CHECK(s.length() == 6 && s.data() == &arr2d[0][0]);
-            auto workaround_macro = [&]() { return s[{1, 2}] == 6; };
-            CHECK(workaround_macro());
-        }
-
-        {
-#ifdef CONFIRM_COMPILATION_ERRORS
-            multi_span<int, 3, 3> s{arr2d};
-#endif
-        }
-
-        int arr3d[2][3][2] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12};
-
-        {
-            multi_span<int> s{arr3d};
-            CHECK(s.length() == 12 && s.data() == &arr3d[0][0][0]);
-            CHECK(s[0] == 1 && s[11] == 12);
-        }
-
-        {
-            multi_span<int, 0> s{arr3d};
-            CHECK(s.length() == 0 && s.data() == &arr3d[0][0][0]);
-        }
-
-        {
-#ifdef CONFIRM_COMPILATION_ERRORS
-            multi_span<int, 11> s{arr3d};
-#endif
-        }
-
-        {
-            multi_span<int, 12> s{arr3d};
-            CHECK(s.length() == 12 && s.data() == &arr3d[0][0][0]);
-            CHECK(s[0] == 1 && s[5] == 6);
-        }
-
-        {
-#ifdef CONFIRM_COMPILATION_ERRORS
-            multi_span<int, 13> s{arr3d};
-#endif
-        }
-
-        {
-            multi_span<int[3][2]> s{arr3d[0]};
-            CHECK(s.length() == 1 && s.data() == &arr3d[0]);
-        }
-
-        {
-            multi_span<int, 3, 2, 2> s{arr3d};
-            CHECK(s.length() == 12 && s.data() == &arr3d[0][0][0]);
-            auto workaround_macro = [&]() { return s[{2, 1, 0}] == 11; };
-            CHECK(workaround_macro());
-        }
-
-        {
-#ifdef CONFIRM_COMPILATION_ERRORS
-            multi_span<int, 3, 3, 3> s{arr3d};
-#endif
-        }
-    }
-
-    TEST(from_dynamic_array_constructor)
-    {
-        double(*arr)[3][4] = new double[100][3][4];
-
-        {
-            multi_span<double, dynamic_range, 3, 4> s(arr, 10);
-            CHECK(s.length() == 120 && s.data() == &arr[0][0][0]);
-            CHECK_THROW(s[10][3][4], fail_fast);
-        }
-
-        {
-            multi_span<double, dynamic_range, 4, 3> s(arr, 10);
-            CHECK(s.length() == 120 && s.data() == &arr[0][0][0]);
-        }
-
-        {
-            multi_span<double> s(arr, 10);
-            CHECK(s.length() == 120 && s.data() == &arr[0][0][0]);
-        }
-
-        {
-            multi_span<double, dynamic_range, 3, 4> s(arr, 0);
-            CHECK(s.length() == 0 && s.data() == &arr[0][0][0]);
-        }
-
-        delete[] arr;
-    }
-
-    TEST(from_std_array_constructor)
-    {
-        std::array<int, 4> arr = {1, 2, 3, 4};
-
-        {
-            multi_span<int> s{arr};
-            CHECK(s.size() == narrow_cast<ptrdiff_t>(arr.size()) && s.data() == arr.data());
-
-            multi_span<const int> cs{arr};
-            CHECK(cs.size() == narrow_cast<ptrdiff_t>(arr.size()) && cs.data() == arr.data());
-        }
-
-        {
-            multi_span<int, 4> s{arr};
-            CHECK(s.size() == narrow_cast<ptrdiff_t>(arr.size()) && s.data() == arr.data());
-
-            multi_span<const int, 4> cs{arr};
-            CHECK(cs.size() == narrow_cast<ptrdiff_t>(arr.size()) && cs.data() == arr.data());
-        }
-
-        {
-            multi_span<int, 2> s{arr};
-            CHECK(s.size() == 2 && s.data() == arr.data());
-
-            multi_span<const int, 2> cs{arr};
-            CHECK(cs.size() == 2 && cs.data() == arr.data());
-        }
-
-        {
-            multi_span<int, 0> s{arr};
-            CHECK(s.size() == 0 && s.data() == arr.data());
-
-            multi_span<const int, 0> cs{arr};
-            CHECK(cs.size() == 0 && cs.data() == arr.data());
-        }
-
-        // TODO This is currently an unsupported scenario. We will come back to it as we revise
-        // the multidimensional interface and what transformations between dimensionality look like
-        //{
-        //    multi_span<int, 2, 2> s{arr};
-        //    CHECK(s.size() == narrow_cast<ptrdiff_t>(arr.size()) && s.data() == arr.data());
-        //}
-
-        {
-#ifdef CONFIRM_COMPILATION_ERRORS
-            multi_span<int, 5> s{arr};
-#endif
-        }
-
-        {
-#ifdef CONFIRM_COMPILATION_ERRORS
-            auto get_an_array = []() { return std::array<int, 4>{1, 2, 3, 4}; };
-            auto take_a_span = [](multi_span<int> s) { (void) s; };
-            // try to take a temporary std::array
-            take_a_span(get_an_array());
-#endif
-        }
-    }
-
-    TEST(from_const_std_array_constructor)
-    {
-        const std::array<int, 4> arr = {1, 2, 3, 4};
-
-        {
-            multi_span<const int> s{arr};
-            CHECK(s.size() == narrow_cast<ptrdiff_t>(arr.size()) && s.data() == arr.data());
-        }
-
-        {
-            multi_span<const int, 4> s{arr};
-            CHECK(s.size() == narrow_cast<ptrdiff_t>(arr.size()) && s.data() == arr.data());
-        }
-
-        {
-            multi_span<const int, 2> s{arr};
-            CHECK(s.size() == 2 && s.data() == arr.data());
-        }
-
-        {
-            multi_span<const int, 0> s{arr};
-            CHECK(s.size() == 0 && s.data() == arr.data());
-        }
-
-        // TODO This is currently an unsupported scenario. We will come back to it as we revise
-        // the multidimensional interface and what transformations between dimensionality look like
-        //{
-        //    multi_span<int, 2, 2> s{arr};
-        //    CHECK(s.size() == narrow_cast<ptrdiff_t>(arr.size()) && s.data() == arr.data());
-        //}
-
-        {
-#ifdef CONFIRM_COMPILATION_ERRORS
-            multi_span<const int, 5> s{arr};
-#endif
-        }
-
-        {
-#ifdef CONFIRM_COMPILATION_ERRORS
-            auto get_an_array = []() -> const std::array<int, 4> { return {1, 2, 3, 4}; };
-            auto take_a_span = [](multi_span<const int> s) { (void) s; };
-            // try to take a temporary std::array
-            take_a_span(get_an_array());
-#endif
-        }
-    }
-
-    TEST(from_container_constructor)
-    {
-        std::vector<int> v = {1, 2, 3};
-        const std::vector<int> cv = v;
-
-        {
-            multi_span<int> s{v};
-            CHECK(s.size() == narrow_cast<std::ptrdiff_t>(v.size()) && s.data() == v.data());
-
-            multi_span<const int> cs{v};
-            CHECK(cs.size() == narrow_cast<std::ptrdiff_t>(v.size()) && cs.data() == v.data());
-        }
-
-        std::string str = "hello";
-        const std::string cstr = "hello";
-
-        {
-#ifdef CONFIRM_COMPILATION_ERRORS
-            multi_span<char> s{str};
-            CHECK(s.size() == narrow_cast<std::ptrdiff_t>(str.size()) && s.data() == str.data());
-#endif
-            multi_span<const char> cs{str};
-            CHECK(cs.size() == narrow_cast<std::ptrdiff_t>(str.size()) && cs.data() == str.data());
-        }
-
-        {
-#ifdef CONFIRM_COMPILATION_ERRORS
-            multi_span<char> s{cstr};
-#endif
-            multi_span<const char> cs{cstr};
-            CHECK(cs.size() == narrow_cast<std::ptrdiff_t>(cstr.size()) &&
-                  cs.data() == cstr.data());
-        }
-
-        {
-#ifdef CONFIRM_COMPILATION_ERRORS
-            auto get_temp_vector = []() -> std::vector<int> { return {}; };
-            auto use_span = [](multi_span<int> s) { (void) s; };
-            use_span(get_temp_vector());
-#endif
-        }
-
-        {
-#ifdef CONFIRM_COMPILATION_ERRORS
-            auto get_temp_string = []() -> std::string { return {}; };
-            auto use_span = [](multi_span<char> s) { (void) s; };
-            use_span(get_temp_string());
-#endif
-        }
-
-        {
-#ifdef CONFIRM_COMPILATION_ERRORS
-            auto get_temp_vector = []() -> const std::vector<int> { return {}; };
-            auto use_span = [](multi_span<const char> s) { (void) s; };
-            use_span(get_temp_vector());
-#endif
-        }
-
-        {
-#ifdef CONFIRM_COMPILATION_ERRORS
-            auto get_temp_string = []() -> const std::string { return {}; };
-            auto use_span = [](multi_span<const char> s) { (void) s; };
-            use_span(get_temp_string());
-#endif
-        }
-
-        {
-#ifdef CONFIRM_COMPILATION_ERRORS
-            std::map<int, int> m;
-            multi_span<int> s{m};
-#endif
-        }
-    }
-
-    TEST(from_convertible_span_constructor)
     {
 #ifdef CONFIRM_COMPILATION_ERRORS
-        multi_span<int, 7, 4, 2> av1(nullptr, b1);
+        multi_span<int, 1> s;
+        CHECK((s.length() == 1 && s.data() == nullptr)); // explains why it can't compile
+#endif
+    }
 
-        auto f = [&]() { multi_span<int, 7, 4, 2> av1(nullptr); };
-        CHECK_THROW(f(), fail_fast);
+    {
+        multi_span<int> s{};
+        CHECK((s.length() == 0 && s.data() == nullptr));
+
+        multi_span<const int> cs{};
+        CHECK((cs.length() == 0 && cs.data() == nullptr));
+    }
+}
+
+TEST_CASE("from_nullptr_constructor")
+{
+    {
+        multi_span<int> s = nullptr;
+        CHECK((s.length() == 0 && s.data() == nullptr));
+
+        multi_span<const int> cs = nullptr;
+        CHECK((cs.length() == 0 && cs.data() == nullptr));
+    }
+
+    {
+        multi_span<int, 0> s = nullptr;
+        CHECK((s.length() == 0 && s.data() == nullptr));
+
+        multi_span<const int, 0> cs = nullptr;
+        CHECK((cs.length() == 0 && cs.data() == nullptr));
+    }
+
+    {
+#ifdef CONFIRM_COMPILATION_ERRORS
+        multi_span<int, 1> s = nullptr;
+        CHECK((s.length() == 1 && s.data() == nullptr)); // explains why it can't compile
+#endif
+    }
+
+    {
+        multi_span<int> s{nullptr};
+        CHECK((s.length() == 0 && s.data() == nullptr));
+
+        multi_span<const int> cs{nullptr};
+        CHECK((cs.length() == 0 && cs.data() == nullptr));
+    }
+
+    {
+        multi_span<int*> s{nullptr};
+        CHECK((s.length() == 0 && s.data() == nullptr));
+
+        multi_span<const int*> cs{nullptr};
+        CHECK((cs.length() == 0 && cs.data() == nullptr));
+    }
+}
+
+TEST_CASE("from_nullptr_length_constructor")
+{
+    {
+        multi_span<int> s{nullptr, 0};
+        CHECK((s.length() == 0 && s.data() == nullptr));
+
+        multi_span<const int> cs{nullptr, 0};
+        CHECK((cs.length() == 0 && cs.data() == nullptr));
+    }
+
+    {
+        multi_span<int, 0> s{nullptr, 0};
+        CHECK((s.length() == 0 && s.data() == nullptr));
+
+        multi_span<const int, 0> cs{nullptr, 0};
+        CHECK((cs.length() == 0 && cs.data() == nullptr));
+    }
+
+    {
+#ifdef CONFIRM_COMPILATION_ERRORS
+        multi_span<int, 1> s{nullptr, 0};
+        CHECK((s.length() == 1 && s.data() == nullptr)); // explains why it can't compile
+#endif
+    }
+
+    {
+        auto workaround_macro = []() { multi_span<int> s{nullptr, 1}; };
+        CHECK_THROWS_AS(workaround_macro(), fail_fast);
+
+        auto const_workaround_macro = []() { multi_span<const int> cs{nullptr, 1}; };
+        CHECK_THROWS_AS(const_workaround_macro(), fail_fast);
+    }
+
+    {
+        auto workaround_macro = []() { multi_span<int, 0> s{nullptr, 1}; };
+        CHECK_THROWS_AS(workaround_macro(), fail_fast);
+
+        auto const_workaround_macro = []() { multi_span<const int, 0> s{nullptr, 1}; };
+        CHECK_THROWS_AS(const_workaround_macro(), fail_fast);
+    }
+
+    {
+        multi_span<int*> s{nullptr, 0};
+        CHECK((s.length() == 0 && s.data() == nullptr));
+
+        multi_span<const int*> cs{nullptr, 0};
+        CHECK((cs.length() == 0 && cs.data() == nullptr));
+    }
+}
+
+TEST_CASE("from_element_constructor")
+{
+    int i = 5;
+
+    {
+        multi_span<int> s = i;
+        CHECK((s.length() == 1 && s.data() == &i));
+        CHECK(s[0] == 5);
+
+        multi_span<const int> cs = i;
+        CHECK((cs.length() == 1 && cs.data() == &i));
+        CHECK(cs[0] == 5);
+    }
+
+    {
+#ifdef CONFIRM_COMPILATION_ERRORS
+        const j = 1;
+        multi_span<int, 0> s = j;
+#endif
+    }
+
+    {
+#ifdef CONFIRM_COMPILATION_ERRORS
+        multi_span<int, 0> s = i;
+        CHECK((s.length() == 0 && s.data() == &i));
+#endif
+    }
+
+    {
+        multi_span<int, 1> s = i;
+        CHECK((s.length() == 1 && s.data() == &i));
+        CHECK(s[0] == 5);
+    }
+
+    {
+#ifdef CONFIRM_COMPILATION_ERRORS
+        multi_span<int, 2> s = i;
+        CHECK((s.length() == 2 && s.data() == &i));
+#endif
+    }
+
+    {
+#ifdef CONFIRM_COMPILATION_ERRORS
+        auto get_a_temp = []() -> int { return 4; };
+        auto use_a_span = [](multi_span<int> s) { (void) s; };
+        use_a_span(get_a_temp());
+#endif
+    }
+}
+
+TEST_CASE("from_pointer_length_constructor")
+{
+    int arr[4] = {1, 2, 3, 4};
+
+    {
+        multi_span<int> s{&arr[0], 2};
+        CHECK((s.length() == 2 && s.data() == &arr[0]));
+        CHECK((s[0] == 1 && s[1] == 2));
+    }
+
+    {
+        multi_span<int, 2> s{&arr[0], 2};
+        CHECK((s.length() == 2 && s.data() == &arr[0]));
+        CHECK((s[0] == 1 && s[1] == 2));
+    }
+
+    {
+        int* p = nullptr;
+        multi_span<int> s{p, 0};
+        CHECK((s.length() == 0 && s.data() == nullptr));
+    }
+
+    {
+        int* p = nullptr;
+        auto workaround_macro = [=]() { multi_span<int> s{p, 2}; };
+        CHECK_THROWS_AS(workaround_macro(), fail_fast);
+    }
+}
+
+TEST_CASE("from_pointer_pointer_constructor")
+{
+    int arr[4] = {1, 2, 3, 4};
+
+    {
+        multi_span<int> s{&arr[0], &arr[2]};
+        CHECK((s.length() == 2 && s.data() == &arr[0]));
+        CHECK((s[0] == 1 && s[1] == 2));
+    }
+
+    {
+        multi_span<int, 2> s{&arr[0], &arr[2]};
+        CHECK((s.length() == 2 && s.data() == &arr[0]));
+        CHECK((s[0] == 1 && s[1] == 2));
+    }
+
+    {
+        multi_span<int> s{&arr[0], &arr[0]};
+        CHECK((s.length() == 0 && s.data() == &arr[0]));
+    }
+
+    {
+        multi_span<int, 0> s{&arr[0], &arr[0]};
+        CHECK((s.length() == 0 && s.data() == &arr[0]));
+    }
+
+    {
+        auto workaround_macro = [&]() { multi_span<int> s{&arr[1], &arr[0]}; };
+        CHECK_THROWS_AS(workaround_macro(), fail_fast);
+    }
+
+    {
+        int* p = nullptr;
+        auto workaround_macro = [&]() { multi_span<int> s{&arr[0], p}; };
+        CHECK_THROWS_AS(workaround_macro(), fail_fast);
+    }
+
+    {
+        int* p = nullptr;
+        auto workaround_macro = [&]() { multi_span<int> s{p, p}; };
+        CHECK_THROWS_AS(workaround_macro(), fail_fast);
+    }
+
+    {
+        int* p = nullptr;
+        auto workaround_macro = [&]() { multi_span<int> s{&arr[0], p}; };
+        CHECK_THROWS_AS(workaround_macro(), fail_fast);
+    }
+}
+
+TEST_CASE("from_array_constructor")
+{
+    int arr[5] = {1, 2, 3, 4, 5};
+
+    {
+        multi_span<int> s{arr};
+        CHECK((s.length() == 5 && s.data() == &arr[0]));
+    }
+
+    {
+        multi_span<int, 5> s{arr};
+        CHECK((s.length() == 5 && s.data() == &arr[0]));
+    }
+
+    {
+#ifdef CONFIRM_COMPILATION_ERRORS
+        multi_span<int, 6> s{arr};
+#endif
+    }
+
+    {
+        multi_span<int, 0> s{arr};
+        CHECK((s.length() == 0 && s.data() == &arr[0]));
+    }
+
+    int arr2d[2][3] = {1, 2, 3, 4, 5, 6};
+
+    {
+        multi_span<int> s{arr2d};
+        CHECK((s.length() == 6 && s.data() == &arr2d[0][0]));
+        CHECK((s[0] == 1 && s[5] == 6));
+    }
+
+    {
+        multi_span<int, 0> s{arr2d};
+        CHECK((s.length() == 0 && s.data() == &arr2d[0][0]));
+    }
+
+    {
+#ifdef CONFIRM_COMPILATION_ERRORS
+        multi_span<int, 5> s{arr2d};
+#endif
+    }
+
+    {
+        multi_span<int, 6> s{arr2d};
+        CHECK((s.length() == 6 && s.data() == &arr2d[0][0]));
+        CHECK((s[0] == 1 && s[5] == 6));
+    }
+
+    {
+#ifdef CONFIRM_COMPILATION_ERRORS
+        multi_span<int, 7> s{arr2d};
+#endif
+    }
+
+    {
+        multi_span<int[3]> s{arr2d[0]};
+        CHECK((s.length() == 1 && s.data() == &arr2d[0]));
+    }
+
+    {
+        multi_span<int, 2, 3> s{arr2d};
+        CHECK((s.length() == 6 && s.data() == &arr2d[0][0]));
+        auto workaround_macro = [&]() { return s[{1, 2}] == 6; };
+        CHECK(workaround_macro());
+    }
+
+    {
+#ifdef CONFIRM_COMPILATION_ERRORS
+        multi_span<int, 3, 3> s{arr2d};
+#endif
+    }
+
+    int arr3d[2][3][2] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12};
+
+    {
+        multi_span<int> s{arr3d};
+        CHECK((s.length() == 12 && s.data() == &arr3d[0][0][0]));
+        CHECK((s[0] == 1 && s[11] == 12));
+    }
+
+    {
+        multi_span<int, 0> s{arr3d};
+        CHECK((s.length() == 0 && s.data() == &arr3d[0][0][0]));
+    }
+
+    {
+#ifdef CONFIRM_COMPILATION_ERRORS
+        multi_span<int, 11> s{arr3d};
+#endif
+    }
+
+    {
+        multi_span<int, 12> s{arr3d};
+        CHECK((s.length() == 12 && s.data() == &arr3d[0][0][0]));
+        CHECK((s[0] == 1 && s[5] == 6));
+    }
+
+    {
+#ifdef CONFIRM_COMPILATION_ERRORS
+        multi_span<int, 13> s{arr3d};
+#endif
+    }
+
+    {
+        multi_span<int[3][2]> s{arr3d[0]};
+        CHECK((s.length() == 1 && s.data() == &arr3d[0]));
+    }
+
+    {
+        multi_span<int, 3, 2, 2> s{arr3d};
+        CHECK((s.length() == 12 && s.data() == &arr3d[0][0][0]));
+        auto workaround_macro = [&]() { return s[{2, 1, 0}] == 11; };
+        CHECK(workaround_macro());
+    }
+
+    {
+#ifdef CONFIRM_COMPILATION_ERRORS
+        multi_span<int, 3, 3, 3> s{arr3d};
+#endif
+    }
+}
+
+TEST_CASE("from_dynamic_array_constructor")
+{
+    double(*arr)[3][4] = new double[100][3][4];
+
+    {
+        multi_span<double, dynamic_range, 3, 4> s(arr, 10);
+        CHECK((s.length() == 120 && s.data() == &arr[0][0][0]));
+        CHECK_THROWS_AS(s[10][3][4], fail_fast);
+    }
+
+    {
+        multi_span<double, dynamic_range, 4, 3> s(arr, 10);
+        CHECK((s.length() == 120 && s.data() == &arr[0][0][0]));
+    }
+
+    {
+        multi_span<double> s(arr, 10);
+        CHECK((s.length() == 120 && s.data() == &arr[0][0][0]));
+    }
+
+    {
+        multi_span<double, dynamic_range, 3, 4> s(arr, 0);
+        CHECK((s.length() == 0 && s.data() == &arr[0][0][0]));
+    }
+
+    delete[] arr;
+}
+
+TEST_CASE("from_std_array_constructor")
+{
+    std::array<int, 4> arr = {1, 2, 3, 4};
+
+    {
+        multi_span<int> s{arr};
+        CHECK((s.size() == narrow_cast<ptrdiff_t>(arr.size()) && s.data() == arr.data()));
+
+        multi_span<const int> cs{arr};
+        CHECK((cs.size() == narrow_cast<ptrdiff_t>(arr.size()) && cs.data() == arr.data()));
+    }
+
+    {
+        multi_span<int, 4> s{arr};
+        CHECK((s.size() == narrow_cast<ptrdiff_t>(arr.size()) && s.data() == arr.data()));
+
+        multi_span<const int, 4> cs{arr};
+        CHECK((cs.size() == narrow_cast<ptrdiff_t>(arr.size()) && cs.data() == arr.data()));
+    }
+
+    {
+        multi_span<int, 2> s{arr};
+        CHECK((s.size() == 2 && s.data() == arr.data()));
+
+        multi_span<const int, 2> cs{arr};
+        CHECK((cs.size() == 2 && cs.data() == arr.data()));
+    }
+
+    {
+        multi_span<int, 0> s{arr};
+        CHECK((s.size() == 0 && s.data() == arr.data()));
+
+        multi_span<const int, 0> cs{arr};
+        CHECK((cs.size() == 0 && cs.data() == arr.data()));
+    }
+
+    // TODO This is currently an unsupported scenario. We will come back to it as we revise
+    // the multidimensional interface and what transformations between dimensionality look like
+    //{
+    //    multi_span<int, 2, 2> s{arr};
+    //    CHECK(s.size() == narrow_cast<ptrdiff_t>(arr.size()) && s.data() == arr.data());
+    //}
+
+    {
+#ifdef CONFIRM_COMPILATION_ERRORS
+        multi_span<int, 5> s{arr};
+#endif
+    }
+
+    {
+#ifdef CONFIRM_COMPILATION_ERRORS
+        auto get_an_array = []() { return std::array<int, 4>{1, 2, 3, 4}; };
+        auto take_a_span = [](multi_span<int> s) { (void) s; };
+        // try to take a temporary std::array
+        take_a_span(get_an_array());
+#endif
+    }
+}
+
+TEST_CASE("from_const_std_array_constructor")
+{
+    const std::array<int, 4> arr = {1, 2, 3, 4};
+
+    {
+        multi_span<const int> s{arr};
+        CHECK((s.size() == narrow_cast<ptrdiff_t>(arr.size()) && s.data() == arr.data()));
+    }
+
+    {
+        multi_span<const int, 4> s{arr};
+        CHECK((s.size() == narrow_cast<ptrdiff_t>(arr.size()) && s.data() == arr.data()));
+    }
+
+    {
+        multi_span<const int, 2> s{arr};
+        CHECK((s.size() == 2 && s.data() == arr.data()));
+    }
+
+    {
+        multi_span<const int, 0> s{arr};
+        CHECK((s.size() == 0 && s.data() == arr.data()));
+    }
+
+    // TODO This is currently an unsupported scenario. We will come back to it as we revise
+    // the multidimensional interface and what transformations between dimensionality look like
+    //{
+    //    multi_span<int, 2, 2> s{arr};
+    //    CHECK(s.size() == narrow_cast<ptrdiff_t>(arr.size()) && s.data() == arr.data());
+    //}
+
+    {
+#ifdef CONFIRM_COMPILATION_ERRORS
+        multi_span<const int, 5> s{arr};
+#endif
+    }
+
+    {
+#ifdef CONFIRM_COMPILATION_ERRORS
+        auto get_an_array = []() -> const std::array<int, 4> { return {1, 2, 3, 4}; };
+        auto take_a_span = [](multi_span<const int> s) { (void) s; };
+        // try to take a temporary std::array
+        take_a_span(get_an_array());
+#endif
+    }
+}
+
+TEST_CASE("from_container_constructor")
+{
+    std::vector<int> v = {1, 2, 3};
+    const std::vector<int> cv = v;
+
+    {
+        multi_span<int> s{v};
+        CHECK((s.size() == narrow_cast<std::ptrdiff_t>(v.size()) && s.data() == v.data()));
+
+        multi_span<const int> cs{v};
+        CHECK((cs.size() == narrow_cast<std::ptrdiff_t>(v.size()) && cs.data() == v.data()));
+    }
+
+    std::string str = "hello";
+    const std::string cstr = "hello";
+
+    {
+#ifdef CONFIRM_COMPILATION_ERRORS
+        multi_span<char> s{str};
+        CHECK((s.size() == narrow_cast<std::ptrdiff_t>(str.size()) && s.data() == str.data()));
+#endif
+        multi_span<const char> cs{str};
+        CHECK((cs.size() == narrow_cast<std::ptrdiff_t>(str.size()) && cs.data() == str.data()));
+    }
+
+    {
+#ifdef CONFIRM_COMPILATION_ERRORS
+        multi_span<char> s{cstr};
+#endif
+        multi_span<const char> cs{cstr};
+        CHECK((cs.size() == narrow_cast<std::ptrdiff_t>(cstr.size()) &&
+               cs.data() == cstr.data()));
+    }
+
+    {
+#ifdef CONFIRM_COMPILATION_ERRORS
+        auto get_temp_vector = []() -> std::vector<int> { return {}; };
+        auto use_span = [](multi_span<int> s) { (void) s; };
+        use_span(get_temp_vector());
+#endif
+    }
+
+    {
+#ifdef CONFIRM_COMPILATION_ERRORS
+        auto get_temp_string = []() -> std::string { return {}; };
+        auto use_span = [](multi_span<char> s) { (void) s; };
+        use_span(get_temp_string());
+#endif
+    }
+
+    {
+#ifdef CONFIRM_COMPILATION_ERRORS
+        auto get_temp_vector = []() -> const std::vector<int> { return {}; };
+        auto use_span = [](multi_span<const char> s) { (void) s; };
+        use_span(get_temp_vector());
+#endif
+    }
+
+    {
+#ifdef CONFIRM_COMPILATION_ERRORS
+        auto get_temp_string = []() -> const std::string { return {}; };
+        auto use_span = [](multi_span<const char> s) { (void) s; };
+        use_span(get_temp_string());
+#endif
+    }
+
+    {
+#ifdef CONFIRM_COMPILATION_ERRORS
+        std::map<int, int> m;
+        multi_span<int> s{m};
+#endif
+    }
+}
+
+TEST_CASE("from_convertible_span_constructor")
+{
+#ifdef CONFIRM_COMPILATION_ERRORS
+    multi_span<int, 7, 4, 2> av1(nullptr, b1);
+
+    auto f = [&]() { multi_span<int, 7, 4, 2> av1(nullptr); };
+    CHECK_THROWS_AS(f(), fail_fast);
 #endif
 
 #ifdef CONFIRM_COMPILATION_ERRORS
-        static_bounds<size_t, 7, dynamic_range, 2> b12(b11);
-        b12 = b11;
-        b11 = b12;
+    static_bounds<std::size_t, 7, dynamic_range, 2> b12(b11);
+    b12 = b11;
+    b11 = b12;
 
-        multi_span<int, dynamic_range> av1 = nullptr;
-        multi_span<int, 7, dynamic_range, 2> av2(av1);
-        multi_span<int, 7, 4, 2> av2(av1);
+    multi_span<int, dynamic_range> av1 = nullptr;
+    multi_span<int, 7, dynamic_range, 2> av2(av1);
+    multi_span<int, 7, 4, 2> av2(av1);
 #endif
 
-        multi_span<DerivedClass> avd;
+    multi_span<DerivedClass> avd;
 #ifdef CONFIRM_COMPILATION_ERRORS
-        multi_span<BaseClass> avb = avd;
+    multi_span<BaseClass> avb = avd;
 #endif
-        multi_span<const DerivedClass> avcd = avd;
-        (void) avcd;
+    multi_span<const DerivedClass> avcd = avd;
+    (void) avcd;
+}
+
+TEST_CASE("copy_move_and_assignment")
+{
+    multi_span<int> s1;
+    CHECK(s1.empty());
+
+    int arr[] = {3, 4, 5};
+
+    multi_span<const int> s2 = arr;
+    CHECK((s2.length() == 3 && s2.data() == &arr[0]));
+
+    s2 = s1;
+    CHECK(s2.empty());
+
+    auto get_temp_span = [&]() -> multi_span<int> { return {&arr[1], 2}; };
+    auto use_span = [&](multi_span<const int> s) {
+        CHECK((s.length() == 2 && s.data() == &arr[1]));
+    };
+    use_span(get_temp_span());
+
+    s1 = get_temp_span();
+    CHECK((s1.length() == 2 && s1.data() == &arr[1]));
+}
+
+template <class Bounds>
+void fn(const Bounds&)
+{
+    static_assert(Bounds::static_size == 60, "static bounds is wrong size");
+}
+TEST_CASE("as_multi_span_reshape")
+{
+    int a[3][4][5];
+    auto av = as_multi_span(a);
+    fn(av.bounds());
+    auto av2 = as_multi_span(av, dim<60>());
+    auto av3 = as_multi_span(av2, dim<3>(), dim<4>(), dim<5>());
+    auto av4 = as_multi_span(av3, dim<4>(), dim(3), dim<5>());
+    auto av5 = as_multi_span(av4, dim<3>(), dim<4>(), dim<5>());
+    auto av6 = as_multi_span(av5, dim<12>(), dim(5));
+
+    fill(av6.begin(), av6.end(), 1);
+
+    auto av7 = as_bytes(av6);
+
+    auto av8 = as_multi_span<int>(av7);
+
+    CHECK(av8.size() == av6.size());
+    for (auto i = 0; i < av8.size(); i++) {
+        CHECK(av8[i] == 1);
+    }
+}
+
+TEST_CASE("first")
+{
+    int arr[5] = {1, 2, 3, 4, 5};
+
+    {
+        multi_span<int, 5> av = arr;
+        CHECK((av.first<2>().bounds() == static_bounds<2>()));
+        CHECK(av.first<2>().length() == 2);
+        CHECK(av.first(2).length() == 2);
     }
 
-    TEST(copy_move_and_assignment)
     {
-        multi_span<int> s1;
-        CHECK(s1.empty());
-
-        int arr[] = {3, 4, 5};
-
-        multi_span<const int> s2 = arr;
-        CHECK(s2.length() == 3 && s2.data() == &arr[0]);
-
-        s2 = s1;
-        CHECK(s2.empty());
-
-        auto get_temp_span = [&]() -> multi_span<int> { return {&arr[1], 2}; };
-        auto use_span = [&](multi_span<const int> s) { CHECK(s.length() == 2 && s.data() == &arr[1]); };
-        use_span(get_temp_span());
-
-        s1 = get_temp_span();
-        CHECK(s1.length() == 2 && s1.data() == &arr[1]);
+        multi_span<int, 5> av = arr;
+        CHECK((av.first<0>().bounds() == static_bounds<0>()));
+        CHECK(av.first<0>().length() == 0);
+        CHECK(av.first(0).length() == 0);
     }
 
-    template <class Bounds>
-    void fn(const Bounds&)
     {
-        static_assert(Bounds::static_size == 60, "static bounds is wrong size");
-    }
-    TEST(as_multi_span_reshape)
-    {
-        int a[3][4][5];
-        auto av = as_multi_span(a);
-        fn(av.bounds());
-        auto av2 = as_multi_span(av, dim<60>());
-        auto av3 = as_multi_span(av2, dim<3>(), dim<4>(), dim<5>());
-        auto av4 = as_multi_span(av3, dim<4>(), dim(3), dim<5>());
-        auto av5 = as_multi_span(av4, dim<3>(), dim<4>(), dim<5>());
-        auto av6 = as_multi_span(av5, dim<12>(), dim(5));
-
-        fill(av6.begin(), av6.end(), 1);
-
-        auto av7 = as_bytes(av6);
-
-        auto av8 = as_multi_span<int>(av7);
-
-        CHECK(av8.size() == av6.size());
-        for (auto i = 0; i < av8.size(); i++) {
-            CHECK(av8[i] == 1);
-        }
+        multi_span<int, 5> av = arr;
+        CHECK((av.first<5>().bounds() == static_bounds<5>()));
+        CHECK(av.first<5>().length() == 5);
+        CHECK(av.first(5).length() == 5);
     }
 
-    TEST(first)
     {
-        int arr[5] = {1, 2, 3, 4, 5};
-
-        {
-            multi_span<int, 5> av = arr;
-            CHECK((av.first<2>().bounds() == static_bounds<2>()));
-            CHECK(av.first<2>().length() == 2);
-            CHECK(av.first(2).length() == 2);
-        }
-
-        {
-            multi_span<int, 5> av = arr;
-            CHECK((av.first<0>().bounds() == static_bounds<0>()));
-            CHECK(av.first<0>().length() == 0);
-            CHECK(av.first(0).length() == 0);
-        }
-
-        {
-            multi_span<int, 5> av = arr;
-            CHECK((av.first<5>().bounds() == static_bounds<5>()));
-            CHECK(av.first<5>().length() == 5);
-            CHECK(av.first(5).length() == 5);
-        }
-
-        {
-            multi_span<int, 5> av = arr;
+        multi_span<int, 5> av = arr;
 #ifdef CONFIRM_COMPILATION_ERRORS
-            CHECK(av.first<6>().bounds() == static_bounds<6>());
-            CHECK(av.first<6>().length() == 6);
-            CHECK(av.first<-1>().length() == -1);
+        CHECK(av.first<6>().bounds() == static_bounds<6>());
+        CHECK(av.first<6>().length() == 6);
+        CHECK(av.first<-1>().length() == -1);
 #endif
-            CHECK_THROW(av.first(6).length(), fail_fast);
-        }
-
-        {
-            multi_span<int, dynamic_range> av;
-            CHECK((av.first<0>().bounds() == static_bounds<0>()));
-            CHECK(av.first<0>().length() == 0);
-            CHECK(av.first(0).length() == 0);
-        }
+        CHECK_THROWS_AS(av.first(6).length(), fail_fast);
     }
 
-    TEST(last)
     {
-        int arr[5] = {1, 2, 3, 4, 5};
+        multi_span<int, dynamic_range> av;
+        CHECK((av.first<0>().bounds() == static_bounds<0>()));
+        CHECK(av.first<0>().length() == 0);
+        CHECK(av.first(0).length() == 0);
+    }
+}
 
-        {
-            multi_span<int, 5> av = arr;
-            CHECK((av.last<2>().bounds() == static_bounds<2>()));
-            CHECK(av.last<2>().length() == 2);
-            CHECK(av.last(2).length() == 2);
-        }
+TEST_CASE("last")
+{
+    int arr[5] = {1, 2, 3, 4, 5};
 
-        {
-            multi_span<int, 5> av = arr;
-            CHECK((av.last<0>().bounds() == static_bounds<0>()));
-            CHECK(av.last<0>().length() == 0);
-            CHECK(av.last(0).length() == 0);
-        }
+    {
+        multi_span<int, 5> av = arr;
+        CHECK((av.last<2>().bounds() == static_bounds<2>()));
+        CHECK(av.last<2>().length() == 2);
+        CHECK(av.last(2).length() == 2);
+    }
 
-        {
-            multi_span<int, 5> av = arr;
-            CHECK((av.last<5>().bounds() == static_bounds<5>()));
-            CHECK(av.last<5>().length() == 5);
-            CHECK(av.last(5).length() == 5);
-        }
+    {
+        multi_span<int, 5> av = arr;
+        CHECK((av.last<0>().bounds() == static_bounds<0>()));
+        CHECK(av.last<0>().length() == 0);
+        CHECK(av.last(0).length() == 0);
+    }
 
-        {
-            multi_span<int, 5> av = arr;
+    {
+        multi_span<int, 5> av = arr;
+        CHECK((av.last<5>().bounds() == static_bounds<5>()));
+        CHECK(av.last<5>().length() == 5);
+        CHECK(av.last(5).length() == 5);
+    }
+
+    {
+        multi_span<int, 5> av = arr;
 #ifdef CONFIRM_COMPILATION_ERRORS
-            CHECK((av.last<6>().bounds() == static_bounds<6>()));
-            CHECK(av.last<6>().length() == 6);
+        CHECK((av.last<6>().bounds() == static_bounds<6>()));
+        CHECK(av.last<6>().length() == 6);
 #endif
-            CHECK_THROW(av.last(6).length(), fail_fast);
-        }
-
-        {
-            multi_span<int, dynamic_range> av;
-            CHECK((av.last<0>().bounds() == static_bounds<0>()));
-            CHECK(av.last<0>().length() == 0);
-            CHECK(av.last(0).length() == 0);
-        }
+        CHECK_THROWS_AS(av.last(6).length(), fail_fast);
     }
 
-    TEST(subspan)
     {
-        int arr[5] = {1, 2, 3, 4, 5};
+        multi_span<int, dynamic_range> av;
+        CHECK((av.last<0>().bounds() == static_bounds<0>()));
+        CHECK(av.last<0>().length() == 0);
+        CHECK(av.last(0).length() == 0);
+    }
+}
 
-        {
-            multi_span<int, 5> av = arr;
-            CHECK((av.subspan<2, 2>().bounds() == static_bounds<2>()));
-            CHECK((av.subspan<2, 2>().length() == 2));
-            CHECK(av.subspan(2, 2).length() == 2);
-            CHECK(av.subspan(2, 3).length() == 3);
-        }
+TEST_CASE("subspan")
+{
+    int arr[5] = {1, 2, 3, 4, 5};
 
-        {
-            multi_span<int, 5> av = arr;
-            CHECK((av.subspan<0, 0>().bounds() == static_bounds<0>()));
-            CHECK((av.subspan<0, 0>().length() == 0));
-            CHECK(av.subspan(0, 0).length() == 0);
-        }
-
-        {
-            multi_span<int, 5> av = arr;
-            CHECK((av.subspan<0, 5>().bounds() == static_bounds<5>()));
-            CHECK((av.subspan<0, 5>().length() == 5));
-            CHECK(av.subspan(0, 5).length() == 5);
-            CHECK_THROW(av.subspan(0, 6).length(), fail_fast);
-            CHECK_THROW(av.subspan(1, 5).length(), fail_fast);
-        }
-
-        {
-            multi_span<int, 5> av = arr;
-            CHECK((av.subspan<5, 0>().bounds() == static_bounds<0>()));
-            CHECK((av.subspan<5, 0>().length() == 0));
-            CHECK(av.subspan(5, 0).length() == 0);
-            CHECK_THROW(av.subspan(6, 0).length(), fail_fast);
-        }
-
-        {
-            multi_span<int, dynamic_range> av;
-            CHECK((av.subspan<0, 0>().bounds() == static_bounds<0>()));
-            CHECK((av.subspan<0, 0>().length() == 0));
-            CHECK(av.subspan(0, 0).length() == 0);
-            CHECK_THROW((av.subspan<1, 0>().length()), fail_fast);
-        }
-
-        {
-            multi_span<int> av;
-            CHECK(av.subspan(0).length() == 0);
-            CHECK_THROW(av.subspan(1).length(), fail_fast);
-        }
-
-        {
-            multi_span<int> av = arr;
-            CHECK(av.subspan(0).length() == 5);
-            CHECK(av.subspan(1).length() == 4);
-            CHECK(av.subspan(4).length() == 1);
-            CHECK(av.subspan(5).length() == 0);
-            CHECK_THROW(av.subspan(6).length(), fail_fast);
-            auto av2 = av.subspan(1);
-            for (int i = 0; i < 4; ++i) CHECK(av2[i] == i + 2);
-        }
-
-        {
-            multi_span<int, 5> av = arr;
-            CHECK(av.subspan(0).length() == 5);
-            CHECK(av.subspan(1).length() == 4);
-            CHECK(av.subspan(4).length() == 1);
-            CHECK(av.subspan(5).length() == 0);
-            CHECK_THROW(av.subspan(6).length(), fail_fast);
-            auto av2 = av.subspan(1);
-            for (int i = 0; i < 4; ++i) CHECK(av2[i] == i + 2);
-        }
+    {
+        multi_span<int, 5> av = arr;
+        CHECK((av.subspan<2, 2>().bounds() == static_bounds<2>()));
+        CHECK((av.subspan<2, 2>().length() == 2));
+        CHECK(av.subspan(2, 2).length() == 2);
+        CHECK(av.subspan(2, 3).length() == 3);
     }
 
-    TEST(rank)
     {
-        int arr[2] = {1, 2};
-
-        {
-            multi_span<int> s;
-            CHECK(s.rank() == 1);
-        }
-
-        {
-            multi_span<int, 2> s = arr;
-            CHECK(s.rank() == 1);
-        }
-
-        int arr2d[1][1] = {};
-        {
-            multi_span<int, 1, 1> s = arr2d;
-            CHECK(s.rank() == 2);
-        }
+        multi_span<int, 5> av = arr;
+        CHECK((av.subspan<0, 0>().bounds() == static_bounds<0>()));
+        CHECK((av.subspan<0, 0>().length() == 0));
+        CHECK(av.subspan(0, 0).length() == 0);
     }
 
-    TEST(extent)
     {
-        {
-            multi_span<int> s;
-            CHECK(s.extent() == 0);
-            CHECK(s.extent(0) == 0);
-            CHECK_THROW(s.extent(1), fail_fast);
+        multi_span<int, 5> av = arr;
+        CHECK((av.subspan<0, 5>().bounds() == static_bounds<5>()));
+        CHECK((av.subspan<0, 5>().length() == 5));
+        CHECK(av.subspan(0, 5).length() == 5);
+        CHECK_THROWS_AS(av.subspan(0, 6).length(), fail_fast);
+        CHECK_THROWS_AS(av.subspan(1, 5).length(), fail_fast);
+    }
+
+    {
+        multi_span<int, 5> av = arr;
+        CHECK((av.subspan<5, 0>().bounds() == static_bounds<0>()));
+        CHECK((av.subspan<5, 0>().length() == 0));
+        CHECK(av.subspan(5, 0).length() == 0);
+        CHECK_THROWS_AS(av.subspan(6, 0).length(), fail_fast);
+    }
+
+    {
+        multi_span<int, dynamic_range> av;
+        CHECK((av.subspan<0, 0>().bounds() == static_bounds<0>()));
+        CHECK((av.subspan<0, 0>().length() == 0));
+        CHECK(av.subspan(0, 0).length() == 0);
+        CHECK_THROWS_AS((av.subspan<1, 0>().length()), fail_fast);
+    }
+
+    {
+        multi_span<int> av;
+        CHECK(av.subspan(0).length() == 0);
+        CHECK_THROWS_AS(av.subspan(1).length(), fail_fast);
+    }
+
+    {
+        multi_span<int> av = arr;
+        CHECK(av.subspan(0).length() == 5);
+        CHECK(av.subspan(1).length() == 4);
+        CHECK(av.subspan(4).length() == 1);
+        CHECK(av.subspan(5).length() == 0);
+        CHECK_THROWS_AS(av.subspan(6).length(), fail_fast);
+        auto av2 = av.subspan(1);
+        for (int i = 0; i < 4; ++i) CHECK(av2[i] == i + 2);
+    }
+
+    {
+        multi_span<int, 5> av = arr;
+        CHECK(av.subspan(0).length() == 5);
+        CHECK(av.subspan(1).length() == 4);
+        CHECK(av.subspan(4).length() == 1);
+        CHECK(av.subspan(5).length() == 0);
+        CHECK_THROWS_AS(av.subspan(6).length(), fail_fast);
+        auto av2 = av.subspan(1);
+        for (int i = 0; i < 4; ++i) CHECK(av2[i] == i + 2);
+    }
+}
+
+TEST_CASE("rank")
+{
+    int arr[2] = {1, 2};
+
+    {
+        multi_span<int> s;
+        CHECK(s.rank() == 1);
+    }
+
+    {
+        multi_span<int, 2> s = arr;
+        CHECK(s.rank() == 1);
+    }
+
+    int arr2d[1][1] = {};
+    {
+        multi_span<int, 1, 1> s = arr2d;
+        CHECK(s.rank() == 2);
+    }
+}
+
+TEST_CASE("extent")
+{
+    {
+        multi_span<int> s;
+        CHECK(s.extent() == 0);
+        CHECK(s.extent(0) == 0);
+        CHECK_THROWS_AS(s.extent(1), fail_fast);
 #ifdef CONFIRM_COMPILATION_ERRORS
-            CHECK(s.extent<1>() == 0);
+        CHECK(s.extent<1>() == 0);
 #endif
-        }
-
-        {
-            multi_span<int, 0> s;
-            CHECK(s.extent() == 0);
-            CHECK(s.extent(0) == 0);
-            CHECK_THROW(s.extent(1), fail_fast);
-        }
-
-        {
-            int arr2d[1][2] = {};
-
-            multi_span<int, 1, 2> s = arr2d;
-            CHECK(s.extent() == 1);
-            CHECK(s.extent<0>() == 1);
-            CHECK(s.extent<1>() == 2);
-            CHECK(s.extent(0) == 1);
-            CHECK(s.extent(1) == 2);
-            CHECK_THROW(s.extent(3), fail_fast);
-        }
-
-        {
-            int arr2d[1][2] = {};
-
-            multi_span<int, 0, 2> s = arr2d;
-            CHECK(s.extent() == 0);
-            CHECK(s.extent<0>() == 0);
-            CHECK(s.extent<1>() == 2);
-            CHECK(s.extent(0) == 0);
-            CHECK(s.extent(1) == 2);
-            CHECK_THROW(s.extent(3), fail_fast);
-        }
     }
 
-    TEST(operator_function_call)
     {
-        int arr[4] = {1, 2, 3, 4};
-
-        {
-            multi_span<int> s = arr;
-            CHECK(s(0) == 1);
-            CHECK_THROW(s(5), fail_fast);
-        }
-
-        int arr2d[2][3] = {1, 2, 3, 4, 5, 6};
-
-        {
-            multi_span<int, 2, 3> s = arr2d;
-            CHECK(s(0, 0) == 1);
-            CHECK(s(1, 2) == 6);
-        }
+        multi_span<int, 0> s;
+        CHECK(s.extent() == 0);
+        CHECK(s.extent(0) == 0);
+        CHECK_THROWS_AS(s.extent(1), fail_fast);
     }
 
-    TEST(comparison_operators)
     {
-        {
-            int arr[10][2];
-            auto s1 = as_multi_span(arr);
-            multi_span<const int, dynamic_range, 2> s2 = s1;
+        int arr2d[1][2] = {};
 
-            CHECK(s1 == s2);
-
-            multi_span<int, 20> s3 = as_multi_span(s1, dim(20));
-            CHECK(s3 == s2 && s3 == s1);
-        }
-
-        {
-            multi_span<int> s1 = nullptr;
-            multi_span<int> s2 = nullptr;
-            CHECK(s1 == s2);
-            CHECK(!(s1 != s2));
-            CHECK(!(s1 < s2));
-            CHECK(s1 <= s2);
-            CHECK(!(s1 > s2));
-            CHECK(s1 >= s2);
-            CHECK(s2 == s1);
-            CHECK(!(s2 != s1));
-            CHECK(!(s2 < s1));
-            CHECK(s2 <= s1);
-            CHECK(!(s2 > s1));
-            CHECK(s2 >= s1);
-        }
-
-        {
-            int arr[] = {2, 1}; // bigger
-
-            multi_span<int> s1 = nullptr;
-            multi_span<int> s2 = arr;
-
-            CHECK(s1 != s2);
-            CHECK(s2 != s1);
-            CHECK(!(s1 == s2));
-            CHECK(!(s2 == s1));
-            CHECK(s1 < s2);
-            CHECK(!(s2 < s1));
-            CHECK(s1 <= s2);
-            CHECK(!(s2 <= s1));
-            CHECK(s2 > s1);
-            CHECK(!(s1 > s2));
-            CHECK(s2 >= s1);
-            CHECK(!(s1 >= s2));
-        }
-
-        {
-            int arr1[] = {1, 2};
-            int arr2[] = {1, 2};
-            multi_span<int> s1 = arr1;
-            multi_span<int> s2 = arr2;
-
-            CHECK(s1 == s2);
-            CHECK(!(s1 != s2));
-            CHECK(!(s1 < s2));
-            CHECK(s1 <= s2);
-            CHECK(!(s1 > s2));
-            CHECK(s1 >= s2);
-            CHECK(s2 == s1);
-            CHECK(!(s2 != s1));
-            CHECK(!(s2 < s1));
-            CHECK(s2 <= s1);
-            CHECK(!(s2 > s1));
-            CHECK(s2 >= s1);
-        }
-
-        {
-            int arr[] = {1, 2, 3};
-
-            multi_span<int> s1 = {&arr[0], 2}; // shorter
-            multi_span<int> s2 = arr; // longer
-
-            CHECK(s1 != s2);
-            CHECK(s2 != s1);
-            CHECK(!(s1 == s2));
-            CHECK(!(s2 == s1));
-            CHECK(s1 < s2);
-            CHECK(!(s2 < s1));
-            CHECK(s1 <= s2);
-            CHECK(!(s2 <= s1));
-            CHECK(s2 > s1);
-            CHECK(!(s1 > s2));
-            CHECK(s2 >= s1);
-            CHECK(!(s1 >= s2));
-        }
-
-        {
-            int arr1[] = {1, 2}; // smaller
-            int arr2[] = {2, 1}; // bigger
-
-            multi_span<int> s1 = arr1;
-            multi_span<int> s2 = arr2;
-
-            CHECK(s1 != s2);
-            CHECK(s2 != s1);
-            CHECK(!(s1 == s2));
-            CHECK(!(s2 == s1));
-            CHECK(s1 < s2);
-            CHECK(!(s2 < s1));
-            CHECK(s1 <= s2);
-            CHECK(!(s2 <= s1));
-            CHECK(s2 > s1);
-            CHECK(!(s1 > s2));
-            CHECK(s2 >= s1);
-            CHECK(!(s1 >= s2));
-        }
+        multi_span<int, 1, 2> s = arr2d;
+        CHECK(s.extent() == 1);
+        CHECK(s.extent<0>() == 1);
+        CHECK(s.extent<1>() == 2);
+        CHECK(s.extent(0) == 1);
+        CHECK(s.extent(1) == 2);
+        CHECK_THROWS_AS(s.extent(3), fail_fast);
     }
 
-    TEST(basics)
     {
-        auto ptr = as_multi_span(new int[10], 10);
-        fill(ptr.begin(), ptr.end(), 99);
-        for (int num : ptr) {
-            CHECK(num == 99);
-        }
+        int arr2d[1][2] = {};
 
-        delete[] ptr.data();
+        multi_span<int, 0, 2> s = arr2d;
+        CHECK(s.extent() == 0);
+        CHECK(s.extent<0>() == 0);
+        CHECK(s.extent<1>() == 2);
+        CHECK(s.extent(0) == 0);
+        CHECK(s.extent(1) == 2);
+        CHECK_THROWS_AS(s.extent(3), fail_fast);
+    }
+}
+
+TEST_CASE("operator_function_call")
+{
+    int arr[4] = {1, 2, 3, 4};
+
+    {
+        multi_span<int> s = arr;
+        CHECK(s(0) == 1);
+        CHECK_THROWS_AS(s(5), fail_fast);
     }
 
-    TEST(bounds_checks)
+    int arr2d[2][3] = {1, 2, 3, 4, 5, 6};
+
+    {
+        multi_span<int, 2, 3> s = arr2d;
+        CHECK(s(0, 0) == 1);
+        CHECK(s(0, 1) == 2);
+        CHECK(s(1, 2) == 6);
+    }
+
+    int arr3d[2][2][2] = {1, 2, 3, 4, 5, 6, 7, 8};
+
+    {
+        multi_span<int, 2, 2, 2> s = arr3d;
+        CHECK(s(0, 0, 0) == 1);
+        CHECK(s(1, 1, 1) == 8);
+    }
+}
+
+TEST_CASE("comparison_operators")
+{
     {
         int arr[10][2];
-        auto av = as_multi_span(arr);
+        auto s1 = as_multi_span(arr);
+        multi_span<const int, dynamic_range, 2> s2 = s1;
 
-        fill(begin(av), end(av), 0);
+        CHECK(s1 == s2);
 
-        av[2][0] = 1;
-        av[1][1] = 3;
-
-        // out of bounds
-        CHECK_THROW(av[1][3] = 3, fail_fast);
-        CHECK_THROW((av[{1, 3}] = 3), fail_fast);
-
-        CHECK_THROW(av[10][2], fail_fast);
-        CHECK_THROW((av[{10, 2}]), fail_fast);
-
-        CHECK_THROW(av[-1][0], fail_fast);
-        CHECK_THROW((av[{-1, 0}]), fail_fast);
-
-        CHECK_THROW(av[0][-1], fail_fast);
-        CHECK_THROW((av[{0, -1}]), fail_fast);
+        multi_span<int, 20> s3 = as_multi_span(s1, dim(20));
+        CHECK((s3 == s2 && s3 == s1));
     }
 
-    void overloaded_func(multi_span<const int, dynamic_range, 3, 5> exp, int expected_value)
     {
-        for (auto val : exp) {
-            CHECK(val == expected_value);
-        }
+        multi_span<int> s1 = nullptr;
+        multi_span<int> s2 = nullptr;
+        CHECK(s1 == s2);
+        CHECK(!(s1 != s2));
+        CHECK(!(s1 < s2));
+        CHECK(s1 <= s2);
+        CHECK(!(s1 > s2));
+        CHECK(s1 >= s2);
+        CHECK(s2 == s1);
+        CHECK(!(s2 != s1));
+        CHECK(!(s2 < s1));
+        CHECK(s2 <= s1);
+        CHECK(!(s2 > s1));
+        CHECK(s2 >= s1);
     }
 
-    void overloaded_func(multi_span<const char, dynamic_range, 3, 5> exp, char expected_value)
     {
-        for (auto val : exp) {
-            CHECK(val == expected_value);
-        }
+        int arr[] = {2, 1}; // bigger
+
+        multi_span<int> s1 = nullptr;
+        multi_span<int> s2 = arr;
+
+        CHECK(s1 != s2);
+        CHECK(s2 != s1);
+        CHECK(!(s1 == s2));
+        CHECK(!(s2 == s1));
+        CHECK(s1 < s2);
+        CHECK(!(s2 < s1));
+        CHECK(s1 <= s2);
+        CHECK(!(s2 <= s1));
+        CHECK(s2 > s1);
+        CHECK(!(s1 > s2));
+        CHECK(s2 >= s1);
+        CHECK(!(s1 >= s2));
     }
 
-    void fixed_func(multi_span<int, 3, 3, 5> exp, int expected_value)
     {
-        for (auto val : exp) {
-            CHECK(val == expected_value);
-        }
+        int arr1[] = {1, 2};
+        int arr2[] = {1, 2};
+        multi_span<int> s1 = arr1;
+        multi_span<int> s2 = arr2;
+
+        CHECK(s1 == s2);
+        CHECK(!(s1 != s2));
+        CHECK(!(s1 < s2));
+        CHECK(s1 <= s2);
+        CHECK(!(s1 > s2));
+        CHECK(s1 >= s2);
+        CHECK(s2 == s1);
+        CHECK(!(s2 != s1));
+        CHECK(!(s2 < s1));
+        CHECK(s2 <= s1);
+        CHECK(!(s2 > s1));
+        CHECK(s2 >= s1);
     }
 
-    TEST(span_parameter_test)
     {
-        auto data = new int[4][3][5];
+        int arr[] = {1, 2, 3};
 
-        auto av = as_multi_span(data, 4);
+        multi_span<int> s1 = {&arr[0], 2}; // shorter
+        multi_span<int> s2 = arr;          // longer
 
-        CHECK(av.size() == 60);
-
-        fill(av.begin(), av.end(), 34);
-
-        int count = 0;
-        for_each(av.rbegin(), av.rend(), [&](int val) { count += val; });
-        CHECK(count == 34 * 60);
-        overloaded_func(av, 34);
-
-        overloaded_func(as_multi_span(av, dim(4), dim(3), dim(5)), 34);
-
-        // fixed_func(av, 34);
-        delete[] data;
+        CHECK(s1 != s2);
+        CHECK(s2 != s1);
+        CHECK(!(s1 == s2));
+        CHECK(!(s2 == s1));
+        CHECK(s1 < s2);
+        CHECK(!(s2 < s1));
+        CHECK(s1 <= s2);
+        CHECK(!(s2 <= s1));
+        CHECK(s2 > s1);
+        CHECK(!(s1 > s2));
+        CHECK(s2 >= s1);
+        CHECK(!(s1 >= s2));
     }
 
-    TEST(md_access)
     {
-        auto width = 5, height = 20;
+        int arr1[] = {1, 2}; // smaller
+        int arr2[] = {2, 1}; // bigger
 
-        auto imgSize = width * height;
-        auto image_ptr = new int[imgSize][3];
+        multi_span<int> s1 = arr1;
+        multi_span<int> s2 = arr2;
 
-        // size check will be done
-        auto image_view =
-            as_multi_span(as_multi_span(image_ptr, imgSize), dim(height), dim(width), dim<3>());
+        CHECK(s1 != s2);
+        CHECK(s2 != s1);
+        CHECK(!(s1 == s2));
+        CHECK(!(s2 == s1));
+        CHECK(s1 < s2);
+        CHECK(!(s2 < s1));
+        CHECK(s1 <= s2);
+        CHECK(!(s2 <= s1));
+        CHECK(s2 > s1);
+        CHECK(!(s1 > s2));
+        CHECK(s2 >= s1);
+        CHECK(!(s1 >= s2));
+    }
+}
 
-        iota(image_view.begin(), image_view.end(), 1);
-
-        int expected = 0;
-        for (auto i = 0; i < height; i++) {
-            for (auto j = 0; j < width; j++) {
-                CHECK(expected + 1 == image_view[i][j][0]);
-                CHECK(expected + 2 == image_view[i][j][1]);
-                CHECK(expected + 3 == image_view[i][j][2]);
-
-                auto val = image_view[{i, j, 0}];
-                CHECK(expected + 1 == val);
-                val = image_view[{i, j, 1}];
-                CHECK(expected + 2 == val);
-                val = image_view[{i, j, 2}];
-                CHECK(expected + 3 == val);
-
-                expected += 3;
-            }
-        }
+TEST_CASE("basics")
+{
+    auto ptr = as_multi_span(new int[10], 10);
+    fill(ptr.begin(), ptr.end(), 99);
+    for (int num : ptr) {
+        CHECK(num == 99);
     }
 
-    TEST(as_multi_span)
-    {
-        {
-            int* arr = new int[150];
+    delete[] ptr.data();
+}
 
-            auto av = as_multi_span(arr, dim<10>(), dim(3), dim<5>());
+TEST_CASE("bounds_checks")
+{
+    int arr[10][2];
+    auto av = as_multi_span(arr);
 
-            fill(av.begin(), av.end(), 24);
-            overloaded_func(av, 24);
+    fill(begin(av), end(av), 0);
 
-            delete[] arr;
+    av[2][0] = 1;
+    av[1][1] = 3;
 
-            array<int, 15> stdarr{0};
-            auto av2 = as_multi_span(stdarr);
-            overloaded_func(as_multi_span(av2, dim(1), dim<3>(), dim<5>()), 0);
+    // out of bounds
+    CHECK_THROWS_AS(av[1][3] = 3, fail_fast);
+    CHECK_THROWS_AS((av[{1, 3}] = 3), fail_fast);
 
-            string str = "ttttttttttttttt"; // size = 15
-            auto t = str.data();
-            (void) t;
-            auto av3 = as_multi_span(str);
-            overloaded_func(as_multi_span(av3, dim(1), dim<3>(), dim<5>()), 't');
-        }
+    CHECK_THROWS_AS(av[10][2], fail_fast);
+    CHECK_THROWS_AS((av[{10, 2}]), fail_fast);
 
-        {
-            string str;
-            multi_span<char> strspan = as_multi_span(str);
-            (void) strspan;
-            const string cstr;
-            multi_span<const char> cstrspan = as_multi_span(cstr);
-            (void) cstrspan;
-        }
+    CHECK_THROWS_AS(av[-1][0], fail_fast);
+    CHECK_THROWS_AS((av[{-1, 0}]), fail_fast);
 
-        {
-            int a[3][4][5];
-            auto av = as_multi_span(a);
-            const int(*b)[4][5];
-            b = a;
-            auto bv = as_multi_span(b, 3);
+    CHECK_THROWS_AS(av[0][-1], fail_fast);
+    CHECK_THROWS_AS((av[{0, -1}]), fail_fast);
+}
 
-            CHECK(av == bv);
-
-            const std::array<double, 3> arr = {0.0, 0.0, 0.0};
-            auto cv = as_multi_span(arr);
-            (void) cv;
-
-            vector<float> vec(3);
-            auto dv = as_multi_span(vec);
-            (void) dv;
-
-#ifdef CONFIRM_COMPILATION_ERRORS
-            auto dv2 = as_multi_span(std::move(vec));
-#endif
-        }
+void overloaded_func(multi_span<const int, dynamic_range, 3, 5> exp, int expected_value)
+{
+    for (auto val : exp) {
+        CHECK(val == expected_value);
     }
+}
 
-    TEST(empty_spans)
-    {
-        {
-            multi_span<int, 0> empty_av(nullptr);
-
-            CHECK(empty_av.bounds().index_bounds() == index<1>{0});
-            CHECK_THROW(empty_av[0], fail_fast);
-            CHECK_THROW(empty_av.begin()[0], fail_fast);
-            CHECK_THROW(empty_av.cbegin()[0], fail_fast);
-            for (auto& v : empty_av) {
-                (void) v;
-                CHECK(false);
-            }
-        }
-
-        {
-            multi_span<int> empty_av = {};
-            CHECK(empty_av.bounds().index_bounds() == index<1>{0});
-            CHECK_THROW(empty_av[0], fail_fast);
-            CHECK_THROW(empty_av.begin()[0], fail_fast);
-            CHECK_THROW(empty_av.cbegin()[0], fail_fast);
-            for (auto& v : empty_av) {
-                (void) v;
-                CHECK(false);
-            }
-        }
+void overloaded_func(multi_span<const char, dynamic_range, 3, 5> exp, char expected_value)
+{
+    for (auto val : exp) {
+        CHECK(val == expected_value);
     }
+}
 
-    TEST(index_constructor)
-    {
-        auto arr = new int[8];
-        for (int i = 0; i < 4; ++i) {
-            arr[2 * i] = 4 + i;
-            arr[2 * i + 1] = i;
-        }
-
-        multi_span<int, dynamic_range> av(arr, 8);
-
-        ptrdiff_t a[1] = {0};
-        index<1> i = a;
-
-        CHECK(av[i] == 4);
-
-        auto av2 = as_multi_span(av, dim<4>(), dim(2));
-        ptrdiff_t a2[2] = {0, 1};
-        index<2> i2 = a2;
-
-        CHECK(av2[i2] == 0);
-        CHECK(av2[0][i] == 4);
-
-        delete[] arr;
+void fixed_func(multi_span<int, 3, 3, 5> exp, int expected_value)
+{
+    for (auto val : exp) {
+        CHECK(val == expected_value);
     }
+}
 
-    TEST(index_constructors)
-    {
-        {
-            // components of the same type
-            index<3> i1(0, 1, 2);
-            CHECK(i1[0] == 0);
+TEST_CASE("span_parameter_test")
+{
+    auto data = new int[4][3][5];
 
-            // components of different types
-            size_t c0 = 0;
-            size_t c1 = 1;
-            index<3> i2(c0, c1, 2);
-            CHECK(i2[0] == 0);
+    auto av = as_multi_span(data, 4);
 
-            // from array
-            index<3> i3 = {0, 1, 2};
-            CHECK(i3[0] == 0);
+    CHECK(av.size() == 60);
 
-            // from other index of the same size type
-            index<3> i4 = i3;
-            CHECK(i4[0] == 0);
+    fill(av.begin(), av.end(), 34);
 
-            // default
-            index<3> i7;
-            CHECK(i7[0] == 0);
+    int count = 0;
+    for_each(av.rbegin(), av.rend(), [&](int val) { count += val; });
+    CHECK(count == 34 * 60);
+    overloaded_func(av, 34);
 
-            // default
-            index<3> i9 = {};
-            CHECK(i9[0] == 0);
-        }
+    overloaded_func(as_multi_span(av, dim(4), dim(3), dim(5)), 34);
 
-        {
-            // components of the same type
-            index<1> i1(0);
-            CHECK(i1[0] == 0);
+    // fixed_func(av, 34);
+    delete[] data;
+}
 
-            // components of different types
-            size_t c0 = 0;
-            index<1> i2(c0);
-            CHECK(i2[0] == 0);
+TEST_CASE("md_access")
+{
+    auto width = 5, height = 20;
 
-            // from array
-            index<1> i3 = {0};
-            CHECK(i3[0] == 0);
+    auto imgSize = width * height;
+    auto image_ptr = new int[static_cast<std::size_t>(imgSize)][3];
 
-            // from int
-            index<1> i4 = 0;
-            CHECK(i4[0] == 0);
+    // size check will be done
+    auto image_view =
+        as_multi_span(as_multi_span(image_ptr, imgSize), dim(height), dim(width), dim<3>());
 
-            // from other index of the same size type
-            index<1> i5 = i3;
-            CHECK(i5[0] == 0);
+    iota(image_view.begin(), image_view.end(), 1);
 
-            // default
-            index<1> i8;
-            CHECK(i8[0] == 0);
+    int expected = 0;
+    for (auto i = 0; i < height; i++) {
+        for (auto j = 0; j < width; j++) {
+            CHECK(expected + 1 == image_view[i][j][0]);
+            CHECK(expected + 2 == image_view[i][j][1]);
+            CHECK(expected + 3 == image_view[i][j][2]);
 
-            // default
-            index<1> i9 = {};
-            CHECK(i9[0] == 0);
-        }
+            auto val = image_view[{i, j, 0}];
+            CHECK(expected + 1 == val);
+            val = image_view[{i, j, 1}];
+            CHECK(expected + 2 == val);
+            val = image_view[{i, j, 2}];
+            CHECK(expected + 3 == val);
 
-#ifdef CONFIRM_COMPILATION_ERRORS
-        {
-            index<3> i1(0, 1);
-            index<3> i2(0, 1, 2, 3);
-            index<3> i3 = {0};
-            index<3> i4 = {0, 1, 2, 3};
-            index<1> i5 = {0, 1};
-        }
-#endif
-    }
-
-    TEST(index_operations)
-    {
-        ptrdiff_t a[3] = {0, 1, 2};
-        ptrdiff_t b[3] = {3, 4, 5};
-        index<3> i = a;
-        index<3> j = b;
-
-        CHECK(i[0] == 0);
-        CHECK(i[1] == 1);
-        CHECK(i[2] == 2);
-
-        {
-            index<3> k = i + j;
-
-            CHECK(i[0] == 0);
-            CHECK(i[1] == 1);
-            CHECK(i[2] == 2);
-            CHECK(k[0] == 3);
-            CHECK(k[1] == 5);
-            CHECK(k[2] == 7);
-        }
-
-        {
-            index<3> k = i * 3;
-
-            CHECK(i[0] == 0);
-            CHECK(i[1] == 1);
-            CHECK(i[2] == 2);
-            CHECK(k[0] == 0);
-            CHECK(k[1] == 3);
-            CHECK(k[2] == 6);
-        }
-
-        {
-            index<3> k = 3 * i;
-
-            CHECK(i[0] == 0);
-            CHECK(i[1] == 1);
-            CHECK(i[2] == 2);
-            CHECK(k[0] == 0);
-            CHECK(k[1] == 3);
-            CHECK(k[2] == 6);
-        }
-
-        {
-            index<2> k = details::shift_left(i);
-
-            CHECK(i[0] == 0);
-            CHECK(i[1] == 1);
-            CHECK(i[2] == 2);
-            CHECK(k[0] == 1);
-            CHECK(k[1] == 2);
-        }
-    }
-
-    void iterate_second_column(multi_span<int, dynamic_range, dynamic_range> av)
-    {
-        auto length = av.size() / 2;
-
-        // view to the second column
-        auto section = av.section({0, 1}, {length, 1});
-
-        CHECK(section.size() == length);
-        for (auto i = 0; i < section.size(); ++i) {
-            CHECK(section[i][0] == av[i][1]);
-        }
-
-        for (auto i = 0; i < section.size(); ++i) {
-            auto idx = index<2>{i, 0}; // avoid braces inside the CHECK macro
-            CHECK(section[idx] == av[i][1]);
-        }
-
-        CHECK(section.bounds().index_bounds()[0] == length);
-        CHECK(section.bounds().index_bounds()[1] == 1);
-        for (auto i = 0; i < section.bounds().index_bounds()[0]; ++i) {
-            for (auto j = 0; j < section.bounds().index_bounds()[1]; ++j) {
-                auto idx = index<2>{i, j}; // avoid braces inside the CHECK macro
-                CHECK(section[idx] == av[i][1]);
-            }
-        }
-
-        size_t check_sum = 0;
-        for (auto i = 0; i < length; ++i) {
-            check_sum += av[i][1];
-        }
-
-        {
-            auto idx = 0;
-            size_t sum = 0;
-            for (auto num : section) {
-                CHECK(num == av[idx][1]);
-                sum += num;
-                idx++;
-            }
-
-            CHECK(sum == check_sum);
-        }
-        {
-            size_t idx = length - 1;
-            size_t sum = 0;
-            for (auto iter = section.rbegin(); iter != section.rend(); ++iter) {
-                CHECK(*iter == av[idx][1]);
-                sum += *iter;
-                idx--;
-            }
-
-            CHECK(sum == check_sum);
-        }
-    }
-
-    TEST(span_section_iteration)
-    {
-        int arr[4][2] = {{4, 0}, {5, 1}, {6, 2}, {7, 3}};
-
-        // static bounds
-        {
-            multi_span<int, 4, 2> av = arr;
-            iterate_second_column(av);
-        }
-        // first bound is dynamic
-        {
-            multi_span<int, dynamic_range, 2> av = arr;
-            iterate_second_column(av);
-        }
-        // second bound is dynamic
-        {
-            multi_span<int, 4, dynamic_range> av = arr;
-            iterate_second_column(av);
-        }
-        // both bounds are dynamic
-        {
-            multi_span<int, dynamic_range, dynamic_range> av = arr;
-            iterate_second_column(av);
-        }
-    }
-
-    TEST(dynamic_span_section_iteration)
-    {
-        auto height = 4, width = 2;
-        auto size = height * width;
-
-        auto arr = new int[size];
-        for (auto i = 0; i < size; ++i) {
-            arr[i] = i;
-        }
-
-        auto av = as_multi_span(arr, size);
-
-        // first bound is dynamic
-        {
-            multi_span<int, dynamic_range, 2> av2 = as_multi_span(av, dim(height), dim(width));
-            iterate_second_column(av2);
-        }
-        // second bound is dynamic
-        {
-            multi_span<int, 4, dynamic_range> av2 = as_multi_span(av, dim(height), dim(width));
-            iterate_second_column(av2);
-        }
-        // both bounds are dynamic
-        {
-            multi_span<int, dynamic_range, dynamic_range> av2 = as_multi_span(av, dim(height), dim(width));
-            iterate_second_column(av2);
-        }
-
-        delete[] arr;
-    }
-
-    TEST(span_structure_size)
-    {
-        double(*arr)[3][4] = new double[100][3][4];
-        multi_span<double, dynamic_range, 3, 4> av1(arr, 10);
-
-        struct EffectiveStructure
-        {
-            double* v1;
-            ptrdiff_t v2;
-        };
-        CHECK(sizeof(av1) == sizeof(EffectiveStructure));
-
-        CHECK_THROW(av1[10][3][4], fail_fast);
-
-        multi_span<const double, dynamic_range, 6, 4> av2 = as_multi_span(av1, dim(5), dim<6>(), dim<4>());
-        (void) av2;
-    }
-
-    TEST(fixed_size_conversions)
-    {
-        int arr[] = {1, 2, 3, 4};
-
-        // converting to an multi_span from an equal size array is ok
-        multi_span<int, 4> av4 = arr;
-        CHECK(av4.length() == 4);
-
-        // converting to dynamic_range a_v is always ok
-        {
-            multi_span<int, dynamic_range> av = av4;
-            (void) av;
-        }
-        {
-            multi_span<int, dynamic_range> av = arr;
-            (void) av;
-        }
-
-// initialization or assignment to static multi_span that REDUCES size is NOT ok
-#ifdef CONFIRM_COMPILATION_ERRORS
-        {
-            multi_span<int, 2> av2 = arr;
-        }
-        {
-            multi_span<int, 2> av2 = av4;
-        }
-#endif
-
-        {
-            multi_span<int, dynamic_range> av = arr;
-            multi_span<int, 2> av2 = av;
-            (void) av2;
-        }
-
-#ifdef CONFIRM_COMPILATION_ERRORS
-        {
-            multi_span<int, dynamic_range> av = arr;
-            multi_span<int, 2, 1> av2 = av.as_multi_span(dim<2>(), dim<2>());
-        }
-#endif
-
-        {
-            multi_span<int, dynamic_range> av = arr;
-            multi_span<int, 2, 1> av2 = as_multi_span(av, dim(2), dim(2));
-            auto workaround_macro = [&]() { return av2[{1, 0}] == 2; };
-            CHECK(workaround_macro());
-        }
-
-        // but doing so explicitly is ok
-
-        // you can convert statically
-        {
-            multi_span<int, 2> av2 = {arr, 2};
-            (void) av2;
-        }
-        {
-            multi_span<int, 1> av2 = av4.first<1>();
-            (void) av2;
-        }
-
-        // ...or dynamically
-        {
-            // NB: implicit conversion to multi_span<int,2> from multi_span<int,dynamic_range>
-            multi_span<int, 1> av2 = av4.first(1);
-            (void) av2;
-        }
-
-        // initialization or assignment to static multi_span that requires size INCREASE is not ok.
-        int arr2[2] = {1, 2};
-
-#ifdef CONFIRM_COMPILATION_ERRORS
-        {
-            multi_span<int, 4> av4 = arr2;
-        }
-        {
-            multi_span<int, 2> av2 = arr2;
-            multi_span<int, 4> av4 = av2;
-        }
-#endif
-        {
-            auto f = [&]() {
-                multi_span<int, 4> av9 = {arr2, 2};
-                (void) av9;
-            };
-            CHECK_THROW(f(), fail_fast);
-        }
-
-        // this should fail - we are trying to assign a small dynamic a_v to a fixed_size larger one
-        multi_span<int, dynamic_range> av = arr2;
-        auto f = [&]() {
-            multi_span<int, 4> av2 = av;
-            (void) av2;
-        };
-        CHECK_THROW(f(), fail_fast);
-    }
-
-    TEST(as_writeable_bytes)
-    {
-        int a[] = {1, 2, 3, 4};
-
-        {
-#ifdef CONFIRM_COMPILATION_ERRORS
-            // you should not be able to get writeable bytes for const objects
-            multi_span<const int, dynamic_range> av = a;
-            auto wav = av.as_writeable_bytes();
-#endif
-        }
-
-        {
-            multi_span<int, dynamic_range> av;
-            auto wav = as_writeable_bytes(av);
-            CHECK(wav.length() == av.length());
-            CHECK(wav.length() == 0);
-            CHECK(wav.size_bytes() == 0);
-        }
-
-        {
-            multi_span<int, dynamic_range> av = a;
-            auto wav = as_writeable_bytes(av);
-            CHECK(wav.data() == (byte*) &a[0]);
-            CHECK(wav.length() == sizeof(a));
-        }
-    }
-
-    TEST(iterator)
-    {
-        int a[] = {1, 2, 3, 4};
-
-        {
-            multi_span<int, dynamic_range> av = a;
-            auto wav = as_writeable_bytes(av);
-            for (auto& b : wav) {
-                b = byte(0);
-            }
-            for (size_t i = 0; i < 4; ++i) {
-                CHECK(a[i] == 0);
-            }
-        }
-
-        {
-            multi_span<int, dynamic_range> av = a;
-            for (auto& n : av) {
-                n = 1;
-            }
-            for (size_t i = 0; i < 4; ++i) {
-                CHECK(a[i] == 1);
-            }
+            expected += 3;
         }
     }
 }
 
-int main(int, const char* []) { return UnitTest::RunAllTests(); }
+TEST_CASE("as_multi_span")
+{
+    {
+        int* arr = new int[150];
+
+        auto av = as_multi_span(arr, dim<10>(), dim(3), dim<5>());
+
+        fill(av.begin(), av.end(), 24);
+        overloaded_func(av, 24);
+
+        delete[] arr;
+
+        array<int, 15> stdarr{0};
+        auto av2 = as_multi_span(stdarr);
+        overloaded_func(as_multi_span(av2, dim(1), dim<3>(), dim<5>()), 0);
+
+        string str = "ttttttttttttttt"; // size = 15
+        auto t = str.data();
+        (void) t;
+        auto av3 = as_multi_span(str);
+        overloaded_func(as_multi_span(av3, dim(1), dim<3>(), dim<5>()), 't');
+    }
+
+    {
+        string str;
+        multi_span<char> strspan = as_multi_span(str);
+        (void) strspan;
+        const string cstr;
+        multi_span<const char> cstrspan = as_multi_span(cstr);
+        (void) cstrspan;
+    }
+
+    {
+        int a[3][4][5];
+        auto av = as_multi_span(a);
+        const int(*b)[4][5];
+        b = a;
+        auto bv = as_multi_span(b, 3);
+
+        CHECK(av == bv);
+
+        const std::array<double, 3> arr = {0.0, 0.0, 0.0};
+        auto cv = as_multi_span(arr);
+        (void) cv;
+
+        vector<float> vec(3);
+        auto dv = as_multi_span(vec);
+        (void) dv;
+
+#ifdef CONFIRM_COMPILATION_ERRORS
+        auto dv2 = as_multi_span(std::move(vec));
+#endif
+    }
+}
+
+TEST_CASE("empty_spans")
+{
+    {
+        multi_span<int, 0> empty_av(nullptr);
+
+        CHECK(empty_av.bounds().index_bounds() == index<1>{0});
+        CHECK_THROWS_AS(empty_av[0], fail_fast);
+        CHECK_THROWS_AS(empty_av.begin()[0], fail_fast);
+        CHECK_THROWS_AS(empty_av.cbegin()[0], fail_fast);
+        for (auto& v : empty_av) {
+            (void) v;
+            CHECK(false);
+        }
+    }
+
+    {
+        multi_span<int> empty_av = {};
+        CHECK(empty_av.bounds().index_bounds() == index<1>{0});
+        CHECK_THROWS_AS(empty_av[0], fail_fast);
+        CHECK_THROWS_AS(empty_av.begin()[0], fail_fast);
+        CHECK_THROWS_AS(empty_av.cbegin()[0], fail_fast);
+        for (auto& v : empty_av) {
+            (void) v;
+            CHECK(false);
+        }
+    }
+}
+
+TEST_CASE("index_constructor")
+{
+    auto arr = new int[8];
+    for (int i = 0; i < 4; ++i) {
+        arr[2 * i] = 4 + i;
+        arr[2 * i + 1] = i;
+    }
+
+    multi_span<int, dynamic_range> av(arr, 8);
+
+    ptrdiff_t a[1] = {0};
+    index<1> i = a;
+
+    CHECK(av[i] == 4);
+
+    auto av2 = as_multi_span(av, dim<4>(), dim(2));
+    ptrdiff_t a2[2] = {0, 1};
+    index<2> i2 = a2;
+
+    CHECK(av2[i2] == 0);
+    CHECK(av2[0][i] == 4);
+
+    delete[] arr;
+}
+
+TEST_CASE("index_constructors")
+{
+    {
+        // components of the same type
+        index<3> i1(0, 1, 2);
+        CHECK(i1[0] == 0);
+
+        // components of different types
+        std::size_t c0 = 0;
+        std::size_t c1 = 1;
+        index<3> i2(c0, c1, 2);
+        CHECK(i2[0] == 0);
+
+        // from array
+        index<3> i3 = {0, 1, 2};
+        CHECK(i3[0] == 0);
+
+        // from other index of the same size type
+        index<3> i4 = i3;
+        CHECK(i4[0] == 0);
+
+        // default
+        index<3> i7;
+        CHECK(i7[0] == 0);
+
+        // default
+        index<3> i9 = {};
+        CHECK(i9[0] == 0);
+    }
+
+    {
+        // components of the same type
+        index<1> i1(0);
+        CHECK(i1[0] == 0);
+
+        // components of different types
+        std::size_t c0 = 0;
+        index<1> i2(c0);
+        CHECK(i2[0] == 0);
+
+        // from array
+        index<1> i3 = {0};
+        CHECK(i3[0] == 0);
+
+        // from int
+        index<1> i4 = 0;
+        CHECK(i4[0] == 0);
+
+        // from other index of the same size type
+        index<1> i5 = i3;
+        CHECK(i5[0] == 0);
+
+        // default
+        index<1> i8;
+        CHECK(i8[0] == 0);
+
+        // default
+        index<1> i9 = {};
+        CHECK(i9[0] == 0);
+    }
+
+    #ifdef CONFIRM_COMPILATION_ERRORS
+    {
+    index<3> i1(0, 1);
+    index<3> i2(0, 1, 2, 3);
+    index<3> i3 = {0};
+    index<3> i4 = {0, 1, 2, 3};
+    index<1> i5 = {0, 1};
+    }
+    #endif
+}
+
+TEST_CASE("index_operations")
+{
+    ptrdiff_t a[3] = {0, 1, 2};
+    ptrdiff_t b[3] = {3, 4, 5};
+    index<3> i = a;
+    index<3> j = b;
+
+    CHECK(i[0] == 0);
+    CHECK(i[1] == 1);
+    CHECK(i[2] == 2);
+
+    {
+        index<3> k = i + j;
+
+        CHECK(i[0] == 0);
+        CHECK(i[1] == 1);
+        CHECK(i[2] == 2);
+        CHECK(k[0] == 3);
+        CHECK(k[1] == 5);
+        CHECK(k[2] == 7);
+    }
+
+    {
+        index<3> k = i * 3;
+
+        CHECK(i[0] == 0);
+        CHECK(i[1] == 1);
+        CHECK(i[2] == 2);
+        CHECK(k[0] == 0);
+        CHECK(k[1] == 3);
+        CHECK(k[2] == 6);
+    }
+
+    {
+        index<3> k = 3 * i;
+
+        CHECK(i[0] == 0);
+        CHECK(i[1] == 1);
+        CHECK(i[2] == 2);
+        CHECK(k[0] == 0);
+        CHECK(k[1] == 3);
+        CHECK(k[2] == 6);
+    }
+
+    {
+        index<2> k = details::shift_left(i);
+
+        CHECK(i[0] == 0);
+        CHECK(i[1] == 1);
+        CHECK(i[2] == 2);
+        CHECK(k[0] == 1);
+        CHECK(k[1] == 2);
+    }
+}
+
+void iterate_second_column(multi_span<int, dynamic_range, dynamic_range> av)
+{
+    auto length = av.size() / 2;
+
+    // view to the second column
+    auto section = av.section({0, 1}, {length, 1});
+
+    CHECK(section.size() == length);
+    for (auto i = 0; i < section.size(); ++i) {
+        CHECK(section[i][0] == av[i][1]);
+    }
+
+    for (auto i = 0; i < section.size(); ++i) {
+        auto idx = index<2>{i, 0}; // avoid braces inside the CHECK macro
+        CHECK(section[idx] == av[i][1]);
+    }
+
+    CHECK(section.bounds().index_bounds()[0] == length);
+    CHECK(section.bounds().index_bounds()[1] == 1);
+    for (auto i = 0; i < section.bounds().index_bounds()[0]; ++i) {
+        for (auto j = 0; j < section.bounds().index_bounds()[1]; ++j) {
+            auto idx = index<2>{i, j}; // avoid braces inside the CHECK macro
+            CHECK(section[idx] == av[i][1]);
+        }
+    }
+
+    auto check_sum = 0;
+    for (auto i = 0; i < length; ++i) {
+        check_sum += av[i][1];
+    }
+
+    {
+        auto idx = 0;
+        auto sum = 0;
+        for (auto num : section) {
+            CHECK(num == av[idx][1]);
+            sum += num;
+            idx++;
+        }
+
+        CHECK(sum == check_sum);
+    }
+    {
+        auto idx = length - 1;
+        auto sum = 0;
+        for (auto iter = section.rbegin(); iter != section.rend(); ++iter) {
+            CHECK(*iter == av[idx][1]);
+            sum += *iter;
+            idx--;
+        }
+
+        CHECK(sum == check_sum);
+    }
+}
+
+TEST_CASE("span_section_iteration")
+{
+    int arr[4][2] = {{4, 0}, {5, 1}, {6, 2}, {7, 3}};
+
+    // static bounds
+    {
+        multi_span<int, 4, 2> av = arr;
+        iterate_second_column(av);
+    }
+    // first bound is dynamic
+    {
+        multi_span<int, dynamic_range, 2> av = arr;
+        iterate_second_column(av);
+    }
+    // second bound is dynamic
+    {
+        multi_span<int, 4, dynamic_range> av = arr;
+        iterate_second_column(av);
+    }
+    // both bounds are dynamic
+    {
+        multi_span<int, dynamic_range, dynamic_range> av = arr;
+        iterate_second_column(av);
+    }
+}
+
+TEST_CASE("dynamic_span_section_iteration")
+{
+    auto height = 4, width = 2;
+    auto size = height * width;
+
+    auto arr = new int[static_cast<std::size_t>(size)];
+    for (auto i = 0; i < size; ++i) {
+        arr[i] = i;
+    }
+
+    auto av = as_multi_span(arr, size);
+
+    // first bound is dynamic
+    {
+        multi_span<int, dynamic_range, 2> av2 = as_multi_span(av, dim(height), dim(width));
+        iterate_second_column(av2);
+    }
+    // second bound is dynamic
+    {
+        multi_span<int, 4, dynamic_range> av2 = as_multi_span(av, dim(height), dim(width));
+        iterate_second_column(av2);
+    }
+    // both bounds are dynamic
+    {
+        multi_span<int, dynamic_range, dynamic_range> av2 =
+            as_multi_span(av, dim(height), dim(width));
+        iterate_second_column(av2);
+    }
+
+    delete[] arr;
+}
+
+TEST_CASE("span_structure_size")
+{
+    double(*arr)[3][4] = new double[100][3][4];
+    multi_span<double, dynamic_range, 3, 4> av1(arr, 10);
+
+    struct EffectiveStructure
+    {
+        double* v1;
+        ptrdiff_t v2;
+    };
+    CHECK(sizeof(av1) == sizeof(EffectiveStructure));
+
+    CHECK_THROWS_AS(av1[10][3][4], fail_fast);
+
+    multi_span<const double, dynamic_range, 6, 4> av2 =
+        as_multi_span(av1, dim(5), dim<6>(), dim<4>());
+    (void) av2;
+}
+
+TEST_CASE("fixed_size_conversions")
+{
+    int arr[] = {1, 2, 3, 4};
+
+    // converting to an multi_span from an equal size array is ok
+    multi_span<int, 4> av4 = arr;
+    CHECK(av4.length() == 4);
+
+    // converting to dynamic_range a_v is always ok
+    {
+        multi_span<int, dynamic_range> av = av4;
+        (void) av;
+    }
+    {
+        multi_span<int, dynamic_range> av = arr;
+        (void) av;
+    }
+
+// initialization or assignment to static multi_span that REDUCES size is NOT ok
+#ifdef CONFIRM_COMPILATION_ERRORS
+    {
+        multi_span<int, 2> av2 = arr;
+    }
+    {
+        multi_span<int, 2> av2 = av4;
+    }
+#endif
+
+    {
+        multi_span<int, dynamic_range> av = arr;
+        multi_span<int, 2> av2 = av;
+        (void) av2;
+    }
+
+#ifdef CONFIRM_COMPILATION_ERRORS
+    {
+        multi_span<int, dynamic_range> av = arr;
+        multi_span<int, 2, 1> av2 = av.as_multi_span(dim<2>(), dim<2>());
+    }
+#endif
+
+    {
+        multi_span<int, dynamic_range> av = arr;
+        multi_span<int, 2, 1> av2 = as_multi_span(av, dim(2), dim(2));
+        auto workaround_macro = [&]() { return av2[{1, 0}] == 2; };
+        CHECK(workaround_macro());
+    }
+
+    // but doing so explicitly is ok
+
+    // you can convert statically
+    {
+        multi_span<int, 2> av2 = {arr, 2};
+        (void) av2;
+    }
+    {
+        multi_span<int, 1> av2 = av4.first<1>();
+        (void) av2;
+    }
+
+    // ...or dynamically
+    {
+        // NB: implicit conversion to multi_span<int,2> from multi_span<int,dynamic_range>
+        multi_span<int, 1> av2 = av4.first(1);
+        (void) av2;
+    }
+
+    // initialization or assignment to static multi_span that requires size INCREASE is not ok.
+    int arr2[2] = {1, 2};
+
+#ifdef CONFIRM_COMPILATION_ERRORS
+    {
+        multi_span<int, 4> av4 = arr2;
+    }
+    {
+        multi_span<int, 2> av2 = arr2;
+        multi_span<int, 4> av4 = av2;
+    }
+#endif
+    {
+        auto f = [&]() {
+            multi_span<int, 4> av9 = {arr2, 2};
+            (void) av9;
+        };
+        CHECK_THROWS_AS(f(), fail_fast);
+    }
+
+    // this should fail - we are trying to assign a small dynamic a_v to a fixed_size larger one
+    multi_span<int, dynamic_range> av = arr2;
+    auto f = [&]() {
+        multi_span<int, 4> av2 = av;
+        (void) av2;
+    };
+    CHECK_THROWS_AS(f(), fail_fast);
+}
+
+TEST_CASE("as_writeable_bytes")
+{
+    int a[] = {1, 2, 3, 4};
+
+    {
+#ifdef CONFIRM_COMPILATION_ERRORS
+        // you should not be able to get writeable bytes for const objects
+        multi_span<const int, dynamic_range> av = a;
+        auto wav = av.as_writeable_bytes();
+#endif
+    }
+
+    {
+        multi_span<int, dynamic_range> av;
+        auto wav = as_writeable_bytes(av);
+        CHECK(wav.length() == av.length());
+        CHECK(wav.length() == 0);
+        CHECK(wav.size_bytes() == 0);
+    }
+
+    {
+        multi_span<int, dynamic_range> av = a;
+        auto wav = as_writeable_bytes(av);
+        CHECK(wav.data() == reinterpret_cast<byte*>(&a[0]));
+        CHECK(static_cast<std::size_t>(wav.length()) == sizeof(a));
+    }
+}
+
+TEST_CASE("iterator")
+{
+    int a[] = {1, 2, 3, 4};
+
+    {
+        multi_span<int, dynamic_range> av = a;
+        auto wav = as_writeable_bytes(av);
+        for (auto& b : wav) {
+            b = byte(0);
+        }
+        for (std::size_t i = 0; i < 4; ++i) {
+            CHECK(a[i] == 0);
+        }
+    }
+
+    {
+        multi_span<int, dynamic_range> av = a;
+        for (auto& n : av) {
+            n = 1;
+        }
+        for (std::size_t i = 0; i < 4; ++i) {
+            CHECK(a[i] == 1);
+        }
+    }
+}
diff --git a/tests/notnull_tests.cpp b/tests/notnull_tests.cpp
index 526b074..62f1e57 100644
--- a/tests/notnull_tests.cpp
+++ b/tests/notnull_tests.cpp
@@ -1,31 +1,41 @@
-/////////////////////////////////////////////////////////////////////////////// 
-// 
-// Copyright (c) 2015 Microsoft Corporation. All rights reserved. 
-// 
-// This code is licensed under the MIT License (MIT). 
-// 
-// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 
-// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 
-// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 
-// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 
-// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 
-// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 
-// THE SOFTWARE. 
-// 
+///////////////////////////////////////////////////////////////////////////////
+//
+// Copyright (c) 2015 Microsoft Corporation. All rights reserved.
+//
+// This code is licensed under the MIT License (MIT).
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+// THE SOFTWARE.
+//
 ///////////////////////////////////////////////////////////////////////////////
 
-#include <UnitTest++/UnitTest++.h> 
+#include <catch/catch.hpp>
+
 #include <gsl/gsl>
+
+#include <memory>
+#include <string>
 #include <vector>
 
 using namespace gsl;
 
-struct MyBase {};
-struct MyDerived : public MyBase {};
-struct Unrelated {};
+struct MyBase
+{
+};
+struct MyDerived : public MyBase
+{
+};
+struct Unrelated
+{
+};
 
 // stand-in for a user-defined ref-counted class
-template<typename T>
+template <typename T>
 struct RefCounted
 {
     RefCounted(T* p) : p_(p) {}
@@ -33,71 +43,243 @@
     T* p_;
 };
 
-SUITE(NotNullTests)
+// user defined smart pointer with comparison operators returning non bool value
+template <typename T>
+struct CustomPtr
 {
+    CustomPtr(T* p) : p_(p) {}
+    operator T*() { return p_; }
+    bool operator!=(std::nullptr_t) const { return p_ != nullptr; }
+    T* p_ = nullptr;
+};
 
-    bool helper(not_null<int*> p)
-    {
-        return *p == 12;
-    }
+template <typename T, typename U>
+std::string operator==(CustomPtr<T> const& lhs, CustomPtr<U> const& rhs)
+{
+    return reinterpret_cast<const void*>(lhs.p_) == reinterpret_cast<const void*>(rhs.p_) ? "true"
+                                                                                          : "false";
+}
 
-    TEST(TestNotNullConstructors)
-    {
+template <typename T, typename U>
+std::string operator!=(CustomPtr<T> const& lhs, CustomPtr<U> const& rhs)
+{
+    return reinterpret_cast<const void*>(lhs.p_) != reinterpret_cast<const void*>(rhs.p_) ? "true"
+                                                                                          : "false";
+}
+
+template <typename T, typename U>
+std::string operator<(CustomPtr<T> const& lhs, CustomPtr<U> const& rhs)
+{
+    return reinterpret_cast<const void*>(lhs.p_) < reinterpret_cast<const void*>(rhs.p_) ? "true"
+                                                                                         : "false";
+}
+
+template <typename T, typename U>
+std::string operator>(CustomPtr<T> const& lhs, CustomPtr<U> const& rhs)
+{
+    return reinterpret_cast<const void*>(lhs.p_) > reinterpret_cast<const void*>(rhs.p_) ? "true"
+                                                                                         : "false";
+}
+
+template <typename T, typename U>
+std::string operator<=(CustomPtr<T> const& lhs, CustomPtr<U> const& rhs)
+{
+    return reinterpret_cast<const void*>(lhs.p_) <= reinterpret_cast<const void*>(rhs.p_) ? "true"
+                                                                                          : "false";
+}
+
+template <typename T, typename U>
+std::string operator>=(CustomPtr<T> const& lhs, CustomPtr<U> const& rhs)
+{
+    return reinterpret_cast<const void*>(lhs.p_) >= reinterpret_cast<const void*>(rhs.p_) ? "true"
+                                                                                          : "false";
+}
+
+struct NonCopyableNonMovable
+{
+    NonCopyableNonMovable() = default;
+    NonCopyableNonMovable(const NonCopyableNonMovable&) = delete;
+    NonCopyableNonMovable& operator=(const NonCopyableNonMovable&) = delete;
+    NonCopyableNonMovable(NonCopyableNonMovable&&) = delete;
+    NonCopyableNonMovable& operator=(NonCopyableNonMovable&&) = delete;
+};
+
+bool helper(not_null<int*> p) { return *p == 12; }
+
+TEST_CASE("TestNotNullConstructors")
+{
 #ifdef CONFIRM_COMPILATION_ERRORS
-        not_null<int*> p = nullptr; // yay...does not compile!
-        not_null<std::vector<char>*> p = 0; // yay...does not compile!
-        not_null<int*> p; // yay...does not compile!
-        std::unique_ptr<int> up = std::make_unique<int>(120);
-        not_null<int*> p = up;
+    not_null<int*> p = nullptr;         // yay...does not compile!
+    not_null<std::vector<char>*> p = 0; // yay...does not compile!
+    not_null<int*> p;                   // yay...does not compile!
+    std::unique_ptr<int> up = std::make_unique<int>(120);
+    not_null<int*> p = up;
 
-        // Forbid non-nullptr assignable types
-        not_null<std::vector<int>> f(std::vector<int>{1});
-        not_null<int> z(10);
-        not_null<std::vector<int>> y({1,2});
+    // Forbid non-nullptr assignable types
+    not_null<std::vector<int>> f(std::vector<int>{1});
+    not_null<int> z(10);
+    not_null<std::vector<int>> y({1, 2});
 #endif
-      int i = 12; 
-      auto rp = RefCounted<int>(&i);
-      not_null<int*> p(rp);
-      CHECK(p.get() == &i);
+    int i = 12;
+    auto rp = RefCounted<int>(&i);
+    not_null<int*> p(rp);
+    CHECK(p.get() == &i);
 
-      not_null<std::shared_ptr<int>> x(std::make_shared<int>(10)); // shared_ptr<int> is nullptr assignable
-    }
+    not_null<std::shared_ptr<int>> x(
+        std::make_shared<int>(10)); // shared_ptr<int> is nullptr assignable
+}
 
-    TEST(TestNotNullCasting)
-    {
-        MyBase base;
-	MyDerived derived;
-	Unrelated unrelated;
-	not_null<Unrelated*> u = &unrelated;
-        (void)u;
-	not_null<MyDerived*> p = &derived;
-        not_null<MyBase*> q = &base;
-	q = p; // allowed with heterogeneous copy ctor
-        CHECK(q == p);
+TEST_CASE("TestNotNullCasting")
+{
+    MyBase base;
+    MyDerived derived;
+    Unrelated unrelated;
+    not_null<Unrelated*> u = &unrelated;
+    (void) u;
+    not_null<MyDerived*> p = &derived;
+    not_null<MyBase*> q = &base;
+    q = p; // allowed with heterogeneous copy ctor
+    CHECK(q == p);
 
 #ifdef CONFIRM_COMPILATION_ERRORS
-	q = u; // no viable conversion possible between MyBase* and Unrelated*
-	p = q; // not possible to implicitly convert MyBase* to MyDerived*
+    q = u; // no viable conversion possible between MyBase* and Unrelated*
+    p = q; // not possible to implicitly convert MyBase* to MyDerived*
 
-        not_null<Unrelated*> r = p;
-        not_null<Unrelated*> s = reinterpret_cast<Unrelated*>(p);
+    not_null<Unrelated*> r = p;
+    not_null<Unrelated*> s = reinterpret_cast<Unrelated*>(p);
 #endif
-        not_null<Unrelated*> t = reinterpret_cast<Unrelated*>(p.get());
-        CHECK((void*)p.get() == (void*)t.get());
+    not_null<Unrelated*> t = reinterpret_cast<Unrelated*>(p.get());
+    CHECK(reinterpret_cast<void*>(p.get()) == reinterpret_cast<void*>(t.get()));
+}
+
+TEST_CASE("TestNotNullAssignment")
+{
+    int i = 12;
+    not_null<int*> p = &i;
+    CHECK(helper(p));
+
+    int* q = nullptr;
+    CHECK_THROWS_AS(p = q, fail_fast);
+}
+
+TEST_CASE("TestNotNullRawPointerComparison")
+{
+    int ints[2] = {42, 43};
+    int* p1 = &ints[0];
+    const int* p2 = &ints[1];
+
+    using NotNull1 = not_null<decltype(p1)>;
+    using NotNull2 = not_null<decltype(p2)>;
+
+    CHECK((NotNull1(p1) == NotNull1(p1)) == true);
+    CHECK((NotNull1(p1) == NotNull2(p2)) == false);
+
+    CHECK((NotNull1(p1) != NotNull1(p1)) == false);
+    CHECK((NotNull1(p1) != NotNull2(p2)) == true);
+
+    CHECK((NotNull1(p1) < NotNull1(p1)) == false);
+    CHECK((NotNull1(p1) < NotNull2(p2)) == (p1 < p2));
+    CHECK((NotNull2(p2) < NotNull1(p1)) == (p2 < p1));
+
+    CHECK((NotNull1(p1) > NotNull1(p1)) == false);
+    CHECK((NotNull1(p1) > NotNull2(p2)) == (p1 > p2));
+    CHECK((NotNull2(p2) > NotNull1(p1)) == (p2 > p1));
+
+    CHECK((NotNull1(p1) <= NotNull1(p1)) == true);
+    CHECK((NotNull1(p1) <= NotNull2(p2)) == (p1 <= p2));
+    CHECK((NotNull2(p2) <= NotNull1(p1)) == (p2 <= p1));
+
+}
+
+TEST_CASE("TestNotNullDereferenceOperator")
+{
+    {
+        auto sp1 = std::make_shared<NonCopyableNonMovable>();
+
+        using NotNullSp1 = not_null<decltype(sp1)>;
+        CHECK(typeid(*sp1) == typeid(*NotNullSp1(sp1))); 
+        CHECK(std::addressof(*NotNullSp1(sp1)) == std::addressof(*sp1));
     }
 
-    TEST(TestNotNullAssignment)
     {
-        int i = 12;
-        not_null<int*> p = &i; 
-        CHECK(helper(p));
+        int ints[1] = { 42 };
+        CustomPtr<int> p1(&ints[0]);
 
-        int* q = nullptr;
-        CHECK_THROW(p = q, fail_fast);
+        using NotNull1 = not_null<decltype(p1)>;
+        CHECK(typeid(*NotNull1(p1)) == typeid(*p1));
+        CHECK(*NotNull1(p1) == 42);
+        *NotNull1(p1) = 43;
+        CHECK(ints[0] == 43);
+    }
+
+    {
+        int v = 42;
+        gsl::not_null<int*> p(&v);
+        CHECK(typeid(*p) == typeid(*(&v)));
+        *p = 43;
+        CHECK(v == 43);
     }
 }
 
-int main(int, const char *[])
+TEST_CASE("TestNotNullSharedPtrComparison")
 {
-    return UnitTest::RunAllTests();
+    auto sp1 = std::make_shared<int>(42);
+    auto sp2 = std::make_shared<const int>(43);
+
+    using NotNullSp1 = not_null<decltype(sp1)>;
+    using NotNullSp2 = not_null<decltype(sp2)>;
+
+    CHECK((NotNullSp1(sp1) == NotNullSp1(sp1)) == true);
+    CHECK((NotNullSp1(sp1) == NotNullSp2(sp2)) == false);
+
+    CHECK((NotNullSp1(sp1) != NotNullSp1(sp1)) == false);
+    CHECK((NotNullSp1(sp1) != NotNullSp2(sp2)) == true);
+
+    CHECK((NotNullSp1(sp1) < NotNullSp1(sp1)) == false);
+    CHECK((NotNullSp1(sp1) < NotNullSp2(sp2)) == (sp1 < sp2));
+    CHECK((NotNullSp2(sp2) < NotNullSp1(sp1)) == (sp2 < sp1));
+
+    CHECK((NotNullSp1(sp1) > NotNullSp1(sp1)) == false);
+    CHECK((NotNullSp1(sp1) > NotNullSp2(sp2)) == (sp1 > sp2));
+    CHECK((NotNullSp2(sp2) > NotNullSp1(sp1)) == (sp2 > sp1));
+
+    CHECK((NotNullSp1(sp1) <= NotNullSp1(sp1)) == true);
+    CHECK((NotNullSp1(sp1) <= NotNullSp2(sp2)) == (sp1 <= sp2));
+    CHECK((NotNullSp2(sp2) <= NotNullSp1(sp1)) == (sp2 <= sp1));
+
+    CHECK((NotNullSp1(sp1) >= NotNullSp1(sp1)) == true);
+    CHECK((NotNullSp1(sp1) >= NotNullSp2(sp2)) == (sp1 >= sp2));
+    CHECK((NotNullSp2(sp2) >= NotNullSp1(sp1)) == (sp2 >= sp1));
+}
+
+TEST_CASE("TestNotNullCustomPtrComparison")
+{
+    int ints[2] = {42, 43};
+    CustomPtr<int> p1(&ints[0]);
+    CustomPtr<const int> p2(&ints[1]);
+
+    using NotNull1 = not_null<decltype(p1)>;
+    using NotNull2 = not_null<decltype(p2)>;
+
+    CHECK((NotNull1(p1) == NotNull1(p1)) == "true");
+    CHECK((NotNull1(p1) == NotNull2(p2)) == "false");
+
+    CHECK((NotNull1(p1) != NotNull1(p1)) == "false");
+    CHECK((NotNull1(p1) != NotNull2(p2)) == "true");
+
+    CHECK((NotNull1(p1) < NotNull1(p1)) == "false");
+    CHECK((NotNull1(p1) < NotNull2(p2)) == (p1 < p2));
+    CHECK((NotNull2(p2) < NotNull1(p1)) == (p2 < p1));
+
+    CHECK((NotNull1(p1) > NotNull1(p1)) == "false");
+    CHECK((NotNull1(p1) > NotNull2(p2)) == (p1 > p2));
+    CHECK((NotNull2(p2) > NotNull1(p1)) == (p2 > p1));
+
+    CHECK((NotNull1(p1) <= NotNull1(p1)) == "true");
+    CHECK((NotNull1(p1) <= NotNull2(p2)) == (p1 <= p2));
+    CHECK((NotNull2(p2) <= NotNull1(p1)) == (p2 <= p1));
+
+    CHECK((NotNull1(p1) >= NotNull1(p1)) == "true");
+    CHECK((NotNull1(p1) >= NotNull2(p2)) == (p1 >= p2));
+    CHECK((NotNull2(p2) >= NotNull1(p1)) == (p2 >= p1));
 }
diff --git a/tests/owner_tests.cpp b/tests/owner_tests.cpp
index 6680981..9885a68 100644
--- a/tests/owner_tests.cpp
+++ b/tests/owner_tests.cpp
@@ -1,43 +1,46 @@
-/////////////////////////////////////////////////////////////////////////////// 
-// 
-// Copyright (c) 2015 Microsoft Corporation. All rights reserved. 
-// 
-// This code is licensed under the MIT License (MIT). 
-// 
-// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 
-// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 
-// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 
-// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 
-// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 
-// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 
-// THE SOFTWARE. 
-// 
+///////////////////////////////////////////////////////////////////////////////
+//
+// Copyright (c) 2015 Microsoft Corporation. All rights reserved.
+//
+// This code is licensed under the MIT License (MIT).
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+// THE SOFTWARE.
+//
 ///////////////////////////////////////////////////////////////////////////////
 
-#include <UnitTest++/UnitTest++.h> 
+#include <catch/catch.hpp>
+
 #include <gsl/gsl>
+
 #include <functional>
 
+#include <memory>
+
 using namespace gsl;
 
-SUITE(owner_tests)
-{
-    void f(int* i)
-    {
-        *i += 1;
-    }
+void f(int* i) { *i += 1; }
 
-    TEST(basic_test)
-    {
-        owner<int*> p = new int(120);
-        CHECK(*p == 120);
-        f(p);
-        CHECK(*p == 121);
-        delete p;
-    }
+TEST_CASE("basic_test")
+{
+    owner<int*> p = new int(120);
+    CHECK(*p == 120);
+    f(p);
+    CHECK(*p == 121);
+    delete p;
 }
 
-int main(int, const char *[])
+TEST_CASE("check_pointer_constraint")
 {
-    return UnitTest::RunAllTests();
+    #ifdef CONFIRM_COMPILATION_ERRORS
+    {
+        owner<int> integerTest = 10;
+        owner<std::shared_ptr<int>> sharedPtrTest(new int(10));
+    }
+    #endif
 }
diff --git a/tests/span_tests.cpp b/tests/span_tests.cpp
index 35e6b03..69a7923 100644
--- a/tests/span_tests.cpp
+++ b/tests/span_tests.cpp
@@ -14,16 +14,17 @@
 //
 ///////////////////////////////////////////////////////////////////////////////
 
-#include <UnitTest++/UnitTest++.h>
+#include <catch/catch.hpp>
+
 #include <gsl/span>
 
 #include <iostream>
 #include <list>
 #include <map>
 #include <memory>
+#include <regex>
 #include <string>
 #include <vector>
-#include <regex>
 
 using namespace std;
 using namespace gsl;
@@ -38,1348 +39,1522 @@
 };
 }
 
-SUITE(span_tests)
+TEST_CASE("default_constructor")
 {
-    TEST(default_constructor)
     {
-        {
-            span<int> s;
-            CHECK(s.length() == 0 && s.data() == nullptr);
+        span<int> s;
+        CHECK((s.length() == 0 && s.data() == nullptr));
 
-            span<const int> cs;
-            CHECK(cs.length() == 0 && cs.data() == nullptr);
-        }
+        span<const int> cs;
+        CHECK((cs.length() == 0 && cs.data() == nullptr));
+    }
 
-        {
-            span<int, 0> s;
-            CHECK(s.length() == 0 && s.data() == nullptr);
+    {
+        span<int, 0> s;
+        CHECK((s.length() == 0 && s.data() == nullptr));
 
-            span<const int, 0> cs;
-            CHECK(cs.length() == 0 && cs.data() == nullptr);
-        }
+        span<const int, 0> cs;
+        CHECK((cs.length() == 0 && cs.data() == nullptr));
+    }
 
-        {
+    {
 #ifdef CONFIRM_COMPILATION_ERRORS
-            span<int, 1> s;
-            CHECK(s.length() == 1 && s.data() == nullptr); // explains why it can't compile
-#endif
-        }
-
-        {
-            span<int> s{};
-            CHECK(s.length() == 0 && s.data() == nullptr);
-
-            span<const int> cs{};
-            CHECK(cs.length() == 0 && cs.data() == nullptr);
-        }
-    }
-
-    TEST(size_optimization)
-    {
-        {
-            span<int> s;
-            CHECK(sizeof(s) == sizeof(int*) + sizeof(ptrdiff_t));
-        }
-
-        {
-            span<int, 0> s;
-            CHECK(sizeof(s) == sizeof(int*));
-        }
-    }
-
-    TEST(from_nullptr_constructor)
-    {
-        {
-            span<int> s = nullptr;
-            CHECK(s.length() == 0 && s.data() == nullptr);
-
-            span<const int> cs = nullptr;
-            CHECK(cs.length() == 0 && cs.data() == nullptr);
-        }
-
-        {
-            span<int, 0> s = nullptr;
-            CHECK(s.length() == 0 && s.data() == nullptr);
-
-            span<const int, 0> cs = nullptr;
-            CHECK(cs.length() == 0 && cs.data() == nullptr);
-        }
-
-        {
-#ifdef CONFIRM_COMPILATION_ERRORS
-            span<int, 1> s = nullptr;
-            CHECK(s.length() == 1 && s.data() == nullptr); // explains why it can't compile
-#endif
-        }
-
-        {
-            span<int> s{nullptr};
-            CHECK(s.length() == 0 && s.data() == nullptr);
-
-            span<const int> cs{nullptr};
-            CHECK(cs.length() == 0 && cs.data() == nullptr);
-        }
-
-        {
-            span<int*> s{nullptr};
-            CHECK(s.length() == 0 && s.data() == nullptr);
-
-            span<const int*> cs{nullptr};
-            CHECK(cs.length() == 0 && cs.data() == nullptr);
-        }
-    }
-
-    TEST(from_nullptr_length_constructor)
-    {
-        {
-            span<int> s{nullptr, static_cast<span<int>::index_type>(0)};
-            CHECK(s.length() == 0 && s.data() == nullptr);
-
-            span<const int> cs{nullptr, static_cast<span<int>::index_type>(0)};
-            CHECK(cs.length() == 0 && cs.data() == nullptr);
-        }
-
-        {
-            span<int, 0> s{nullptr, static_cast<span<int>::index_type>(0)};
-            CHECK(s.length() == 0 && s.data() == nullptr);
-
-            span<const int, 0> cs{nullptr, static_cast<span<int>::index_type>(0)};
-            CHECK(cs.length() == 0 && cs.data() == nullptr);
-        }
-
-        {
-            auto workaround_macro = []() { span<int, 1> s{ nullptr, static_cast<span<int>::index_type>(0) }; };
-            CHECK_THROW(workaround_macro(), fail_fast); 
-        }
-
-        {
-            auto workaround_macro = []() { span<int> s{nullptr, 1}; };
-            CHECK_THROW(workaround_macro(), fail_fast);
-
-            auto const_workaround_macro = []() { span<const int> cs{nullptr, 1}; };
-            CHECK_THROW(const_workaround_macro(), fail_fast);
-        }
-
-        {
-            auto workaround_macro = []() { span<int, 0> s{nullptr, 1}; };
-            CHECK_THROW(workaround_macro(), fail_fast);
-
-            auto const_workaround_macro = []() { span<const int, 0> s{nullptr, 1}; };
-            CHECK_THROW(const_workaround_macro(), fail_fast);
-        }
-
-        {
-            span<int*> s{nullptr, static_cast<span<int>::index_type>(0)};
-            CHECK(s.length() == 0 && s.data() == nullptr);
-
-            span<const int*> cs{nullptr, static_cast<span<int>::index_type>(0)};
-            CHECK(cs.length() == 0 && cs.data() == nullptr);
-        }
-    }
-
-    TEST(from_pointer_length_constructor)
-    {
-        int arr[4] = {1, 2, 3, 4};
-
-        {
-            span<int> s{&arr[0], 2};
-            CHECK(s.length() == 2 && s.data() == &arr[0]);
-            CHECK(s[0] == 1 && s[1] == 2);
-        }
-
-        {
-            span<int, 2> s{&arr[0], 2};
-            CHECK(s.length() == 2 && s.data() == &arr[0]);
-            CHECK(s[0] == 1 && s[1] == 2);
-        }
-
-        {
-            int* p = nullptr;
-            span<int> s{p, static_cast<span<int>::index_type>(0)};
-            CHECK(s.length() == 0 && s.data() == nullptr);
-        }
-
-        {
-            int* p = nullptr;
-            auto workaround_macro = [=]() { span<int> s{p, 2}; };
-            CHECK_THROW(workaround_macro(), fail_fast);
-        }
-    }
-
-    TEST(from_pointer_pointer_constructor)
-    {
-        int arr[4] = {1, 2, 3, 4};
-
-        {
-            span<int> s{&arr[0], &arr[2]};
-            CHECK(s.length() == 2 && s.data() == &arr[0]);
-            CHECK(s[0] == 1 && s[1] == 2);
-        }
-
-        {
-            span<int, 2> s{&arr[0], &arr[2]};
-            CHECK(s.length() == 2 && s.data() == &arr[0]);
-            CHECK(s[0] == 1 && s[1] == 2);
-        }
-
-        {
-            span<int> s{&arr[0], &arr[0]};
-            CHECK(s.length() == 0 && s.data() == &arr[0]);
-        }
-
-        {
-            span<int, 0> s{&arr[0], &arr[0]};
-            CHECK(s.length() == 0 && s.data() == &arr[0]);
-        }
-
-        // this will fail the std::distance() precondition, which asserts on MSVC debug builds
-        //{
-        //    auto workaround_macro = [&]() { span<int> s{&arr[1], &arr[0]}; };
-        //    CHECK_THROW(workaround_macro(), fail_fast);
-        //}
-
-        // this will fail the std::distance() precondition, which asserts on MSVC debug builds
-        //{
-        //    int* p = nullptr;
-        //    auto workaround_macro = [&]() { span<int> s{&arr[0], p}; };
-        //    CHECK_THROW(workaround_macro(), fail_fast);
-        //}
-
-        {
-            int* p = nullptr;
-            span<int> s{ p, p };
-            CHECK(s.length() == 0 && s.data() == nullptr);
-        }
-
-        {
-            int* p = nullptr;
-            span<int, 0> s{ p, p };
-            CHECK(s.length() == 0 && s.data() == nullptr);
-        }
-
-        // this will fail the std::distance() precondition, which asserts on MSVC debug builds
-        //{
-        //    int* p = nullptr;
-        //    auto workaround_macro = [&]() { span<int> s{&arr[0], p}; };
-        //    CHECK_THROW(workaround_macro(), fail_fast);
-        //}
-    }
-
-    TEST(from_array_constructor)
-    {
-        int arr[5] = {1, 2, 3, 4, 5};
-
-        {
-            span<int> s{arr};
-            CHECK(s.length() == 5 && s.data() == &arr[0]);
-        }
-
-        {
-            span<int, 5> s{arr};
-            CHECK(s.length() == 5 && s.data() == &arr[0]);
-        }
-
-        int arr2d[2][3] = { 1, 2, 3, 4, 5, 6 };
-
-#ifdef CONFIRM_COMPILATION_ERRORS
-        {
-            span<int, 6> s{arr};
-        }
-
-        {
-            span<int, 0> s{arr};
-            CHECK(s.length() == 0 && s.data() == &arr[0]);
-        }
-
-        {
-            span<int> s{arr2d};
-            CHECK(s.length() == 6 && s.data() == &arr2d[0][0]);
-            CHECK(s[0] == 1 && s[5] == 6);
-        }
-
-        {
-            span<int, 0> s{arr2d};
-            CHECK(s.length() == 0 && s.data() == &arr2d[0][0]);
-        }
-
-        {
-            span<int, 6> s{ arr2d };
-        }
-#endif
-        {
-            span<int[3]> s{ &(arr2d[0]), 1 };
-            CHECK(s.length() == 1 && s.data() == &arr2d[0]);
-        }
-
-        int arr3d[2][3][2] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12};
-
-#ifdef CONFIRM_COMPILATION_ERRORS
-        {
-            span<int> s{arr3d};
-            CHECK(s.length() == 12 && s.data() == &arr3d[0][0][0]);
-            CHECK(s[0] == 1 && s[11] == 12);
-        }
-
-        {
-            span<int, 0> s{arr3d};
-            CHECK(s.length() == 0 && s.data() == &arr3d[0][0][0]);
-        }
-
-        {
-            span<int, 11> s{arr3d};
-        }
-
-        {
-            span<int, 12> s{arr3d};
-            CHECK(s.length() == 12 && s.data() == &arr3d[0][0][0]);
-            CHECK(s[0] == 1 && s[5] == 6);
-        }
-#endif
-        {
-            span<int[3][2]> s{&arr3d[0], 1};
-            CHECK(s.length() == 1 && s.data() == &arr3d[0]);
-        }
-    }
-
-    TEST(from_dynamic_array_constructor)
-    {
-        double(*arr)[3][4] = new double[100][3][4];
-
-        {
-            span<double> s(&arr[0][0][0], 10);
-            CHECK(s.length() == 10 && s.data() == &arr[0][0][0]);
-        }
-
-        delete[] arr;
-    }
-
-    TEST(from_std_array_constructor)
-    {
-        std::array<int, 4> arr = {1, 2, 3, 4};
-
-        {
-            span<int> s{arr};
-            CHECK(s.size() == narrow_cast<ptrdiff_t>(arr.size()) && s.data() == arr.data());
-
-            span<const int> cs{arr};
-            CHECK(cs.size() == narrow_cast<ptrdiff_t>(arr.size()) && cs.data() == arr.data());
-        }
-
-        {
-            span<int, 4> s{arr};
-            CHECK(s.size() == narrow_cast<ptrdiff_t>(arr.size()) && s.data() == arr.data());
-
-            span<const int, 4> cs{arr};
-            CHECK(cs.size() == narrow_cast<ptrdiff_t>(arr.size()) && cs.data() == arr.data());
-        }
-
-#ifdef CONFIRM_COMPILATION_ERRORS
-        {
-            span<int, 2> s{arr};
-            CHECK(s.size() == 2 && s.data() == arr.data());
-
-            span<const int, 2> cs{arr};
-            CHECK(cs.size() == 2 && cs.data() == arr.data());
-        }
-
-        {
-            span<int, 0> s{arr};
-            CHECK(s.size() == 0 && s.data() == arr.data());
-
-            span<const int, 0> cs{arr};
-            CHECK(cs.size() == 0 && cs.data() == arr.data());
-        }
-
-        {
-            span<int, 5> s{arr};
-        }
-
-        {
-            auto get_an_array = []()->std::array<int, 4> { return{1, 2, 3, 4}; };
-            auto take_a_span = [](span<int> s) { static_cast<void>(s); };
-            // try to take a temporary std::array
-            take_a_span(get_an_array());
-        }
-#endif
-
-        {
-            auto get_an_array = []() -> std::array<int, 4> { return { 1, 2, 3, 4 }; };
-            auto take_a_span = [](span<const int> s) { static_cast<void>(s); };
-            // try to take a temporary std::array
-            take_a_span(get_an_array());
-        }
-    }
-
-    TEST(from_const_std_array_constructor)
-    {
-        const std::array<int, 4> arr = {1, 2, 3, 4};
-
-        {
-            span<const int> s{arr};
-            CHECK(s.size() == narrow_cast<ptrdiff_t>(arr.size()) && s.data() == arr.data());
-        }
-
-        {
-            span<const int, 4> s{arr};
-            CHECK(s.size() == narrow_cast<ptrdiff_t>(arr.size()) && s.data() == arr.data());
-        }
-
-#ifdef CONFIRM_COMPILATION_ERRORS
-        {
-            span<const int, 2> s{arr};
-            CHECK(s.size() == 2 && s.data() == arr.data());
-        }
-
-        {
-            span<const int, 0> s{arr};
-            CHECK(s.size() == 0 && s.data() == arr.data());
-        }
-
-        {
-            span<const int, 5> s{arr};
-        }
-#endif
-
-        {
-            auto get_an_array = []() -> const std::array<int, 4> { return {1, 2, 3, 4}; };
-            auto take_a_span = [](span<const int> s) { static_cast<void>(s); };
-            // try to take a temporary std::array
-            take_a_span(get_an_array());
-        }
-    }
-
-    TEST(from_std_array_const_constructor)
-    {
-        std::array<const int, 4> arr = {1, 2, 3, 4};
-
-        {
-            span<const int> s{arr};
-            CHECK(s.size() == narrow_cast<ptrdiff_t>(arr.size()) && s.data() == arr.data());
-        }
-
-        {
-            span<const int, 4> s{arr};
-            CHECK(s.size() == narrow_cast<ptrdiff_t>(arr.size()) && s.data() == arr.data());
-        }
-
-#ifdef CONFIRM_COMPILATION_ERRORS
-        {
-            span<const int, 2> s{arr};
-            CHECK(s.size() == 2 && s.data() == arr.data());
-        }
-
-        {
-            span<const int, 0> s{arr};
-            CHECK(s.size() == 0 && s.data() == arr.data());
-        }
-
-        {
-            span<const int, 5> s{arr};
-        }
-
-        {
-            span<int, 4> s{arr};
-        }
+        span<int, 1> s;
+        CHECK((s.length() == 1 && s.data() == nullptr)); // explains why it can't compile
 #endif
     }
 
-    TEST(from_container_constructor)
     {
-        std::vector<int> v = {1, 2, 3};
-        const std::vector<int> cv = v;
+        span<int> s{};
+        CHECK((s.length() == 0 && s.data() == nullptr));
 
-        {
-            span<int> s{v};
-            CHECK(s.size() == narrow_cast<std::ptrdiff_t>(v.size()) && s.data() == v.data());
-
-            span<const int> cs{v};
-            CHECK(cs.size() == narrow_cast<std::ptrdiff_t>(v.size()) && cs.data() == v.data());
-        }
-
-        std::string str = "hello";
-        const std::string cstr = "hello";
-
-        {
-#ifdef CONFIRM_COMPILATION_ERRORS
-            span<char> s{str};
-            CHECK(s.size() == narrow_cast<std::ptrdiff_t>(str.size()) && s.data() == str.data());
-#endif
-            span<const char> cs{str};
-            CHECK(cs.size() == narrow_cast<std::ptrdiff_t>(str.size()) && cs.data() == str.data());
-        }
-
-        {
-#ifdef CONFIRM_COMPILATION_ERRORS
-            span<char> s{cstr};
-#endif                                                          
-            span<const char> cs{cstr};
-            CHECK(cs.size() == narrow_cast<std::ptrdiff_t>(cstr.size()) &&
-                  cs.data() == cstr.data());
-        }
-
-        {
-#ifdef CONFIRM_COMPILATION_ERRORS
-            auto get_temp_vector = []() -> std::vector<int> { return {}; };
-            auto use_span = [](span<int> s) { static_cast<void>(s); };
-            use_span(get_temp_vector());
-#endif                      
-        }
-
-        {
-            auto get_temp_vector = []() -> std::vector<int> { return{}; };
-            auto use_span = [](span<const int> s) { static_cast<void>(s); };
-            use_span(get_temp_vector());
-        }
-
-        {
-#ifdef CONFIRM_COMPILATION_ERRORS
-            auto get_temp_string = []() -> std::string { return{}; };
-            auto use_span = [](span<char> s) { static_cast<void>(s); };
-            use_span(get_temp_string());
-#endif                         
-        }
-
-        {
-            auto get_temp_string = []() -> std::string { return {}; };
-            auto use_span = [](span<const char> s) { static_cast<void>(s); };
-            use_span(get_temp_string());
-        }
-
-        {
-#ifdef CONFIRM_COMPILATION_ERRORS
-            auto get_temp_vector = []() -> const std::vector<int> { return {}; };
-            auto use_span = [](span<const char> s) { static_cast<void>(s); };
-            use_span(get_temp_vector());
-#endif
-        }
-
-        {
-            auto get_temp_string = []() -> const std::string { return {}; };
-            auto use_span = [](span<const char> s) { static_cast<void>(s); };
-            use_span(get_temp_string());
-        }
-
-        {
-#ifdef CONFIRM_COMPILATION_ERRORS
-            std::map<int, int> m;
-            span<int> s{m};
-#endif
-        }
-    }
-
-    TEST(from_convertible_span_constructor)
-    {
-        {
-            span<DerivedClass> avd;
-            span<const DerivedClass> avcd = avd;
-            static_cast<void>(avcd);
-        }
-
-        {
-#ifdef CONFIRM_COMPILATION_ERRORS
-            span<DerivedClass> avd;
-            span<BaseClass> avb = avd;
-            static_cast<void>(avb);
-#endif
-        }
-
-#ifdef CONFIRM_COMPILATION_ERRORS
-        {
-            span<int> s;
-            span<unsigned int> s2 = s;
-            static_cast<void>(s2);
-        }
-
-        {
-            span<int> s;
-            span<const unsigned int> s2 = s;
-            static_cast<void>(s2);
-        }
-
-        {
-            span<int> s;
-            span<short> s2 = s;
-            static_cast<void>(s2);
-        }
-#endif
-    }
-
-    TEST(copy_move_and_assignment)
-    {
-        span<int> s1;
-        CHECK(s1.empty());
-
-        int arr[] = {3, 4, 5};
-
-        span<const int> s2 = arr;
-        CHECK(s2.length() == 3 && s2.data() == &arr[0]);
-
-        s2 = s1;
-        CHECK(s2.empty());
-
-        auto get_temp_span = [&]() -> span<int> { return {&arr[1], 2}; };
-        auto use_span = [&](span<const int> s) { CHECK(s.length() == 2 && s.data() == &arr[1]); };
-        use_span(get_temp_span());
-
-        s1 = get_temp_span();
-        CHECK(s1.length() == 2 && s1.data() == &arr[1]);
-    }
-
-    TEST(first)
-    {
-        int arr[5] = {1, 2, 3, 4, 5};
-
-        {
-            span<int, 5> av = arr;
-            CHECK(av.first<2>().length() == 2);
-            CHECK(av.first(2).length() == 2);
-        }
-
-        {
-            span<int, 5> av = arr;
-            CHECK(av.first<0>().length() == 0);
-            CHECK(av.first(0).length() == 0);
-        }
-
-        {
-            span<int, 5> av = arr;
-            CHECK(av.first<5>().length() == 5);
-            CHECK(av.first(5).length() == 5);
-        }
-
-        {
-            span<int, 5> av = arr;
-#ifdef CONFIRM_COMPILATION_ERRORS
-            CHECK(av.first<6>().length() == 6);
-            CHECK(av.first<-1>().length() == -1);
-#endif
-            CHECK_THROW(av.first(6).length(), fail_fast);
-        }
-
-        {
-            span<int> av;
-            CHECK(av.first<0>().length() == 0);
-            CHECK(av.first(0).length() == 0);
-        }
-    }
-
-    TEST(last)
-    {
-        int arr[5] = {1, 2, 3, 4, 5};
-
-        {
-            span<int, 5> av = arr;
-            CHECK(av.last<2>().length() == 2);
-            CHECK(av.last(2).length() == 2);
-        }
-
-        {
-            span<int, 5> av = arr;
-            CHECK(av.last<0>().length() == 0);
-            CHECK(av.last(0).length() == 0);
-        }
-
-        {
-            span<int, 5> av = arr;
-            CHECK(av.last<5>().length() == 5);
-            CHECK(av.last(5).length() == 5);
-        }
-
-        {
-            span<int, 5> av = arr;
-#ifdef CONFIRM_COMPILATION_ERRORS
-            CHECK(av.last<6>().length() == 6);
-#endif    
-            CHECK_THROW(av.last(6).length(), fail_fast);
-        }
-
-        {
-            span<int> av;
-            CHECK(av.last<0>().length() == 0);
-            CHECK(av.last(0).length() == 0);
-        }
-    }
-
-    TEST(subspan)
-    {
-        int arr[5] = {1, 2, 3, 4, 5};
-
-        {
-            span<int, 5> av = arr;
-            CHECK((av.subspan<2, 2>().length() == 2));
-            CHECK(av.subspan(2, 2).length() == 2);
-            CHECK(av.subspan(2, 3).length() == 3);
-        }
-
-        {
-            span<int, 5> av = arr;
-            CHECK((av.subspan<0, 0>().length() == 0));
-            CHECK(av.subspan(0, 0).length() == 0);
-        }
-
-        {
-            span<int, 5> av = arr;
-            CHECK((av.subspan<0, 5>().length() == 5));
-            CHECK(av.subspan(0, 5).length() == 5);
-            CHECK_THROW(av.subspan(0, 6).length(), fail_fast);
-            CHECK_THROW(av.subspan(1, 5).length(), fail_fast);
-        }
-
-        {
-            span<int, 5> av = arr;
-            CHECK((av.subspan<4, 0>().length() == 0));
-            CHECK(av.subspan(4, 0).length() == 0);
-            CHECK(av.subspan(5, 0).length() == 0);
-            CHECK_THROW(av.subspan(6, 0).length(), fail_fast);
-        }
-
-        {
-            span<int> av;
-            CHECK((av.subspan<0, 0>().length() == 0));
-            CHECK(av.subspan(0, 0).length() == 0);
-            CHECK_THROW((av.subspan<1, 0>().length()), fail_fast);
-        }
-
-        {
-            span<int> av;
-            CHECK(av.subspan(0).length() == 0);
-            CHECK_THROW(av.subspan(1).length(), fail_fast);
-        }
-
-        {
-            span<int> av = arr;
-            CHECK(av.subspan(0).length() == 5);
-            CHECK(av.subspan(1).length() == 4);
-            CHECK(av.subspan(4).length() == 1);
-            CHECK(av.subspan(5).length() == 0);
-            CHECK_THROW(av.subspan(6).length(), fail_fast);
-            auto av2 = av.subspan(1);
-            for (int i = 0; i < 4; ++i) CHECK(av2[i] == i + 2);
-        }
-
-        {
-            span<int, 5> av = arr;
-            CHECK(av.subspan(0).length() == 5);
-            CHECK(av.subspan(1).length() == 4);
-            CHECK(av.subspan(4).length() == 1);
-            CHECK(av.subspan(5).length() == 0);
-            CHECK_THROW(av.subspan(6).length(), fail_fast);
-            auto av2 = av.subspan(1);
-            for (int i = 0; i < 4; ++i) CHECK(av2[i] == i + 2);
-        }
-    }
-
-    TEST(at_call)
-    {
-        int arr[4] = {1, 2, 3, 4};
-
-        {
-            span<int> s = arr;
-            CHECK(s.at(0) == 1);
-            CHECK_THROW(s.at(5), fail_fast);
-        }
-
-        {
-            int arr2d[2] = {1, 6};
-            span<int, 2> s = arr2d;
-            CHECK(s.at(0) == 1);
-            CHECK(s.at(1) == 6);
-            CHECK_THROW(s.at(2) ,fail_fast);
-        }
-    }
-
-    TEST(operator_function_call)
-    {
-        int arr[4] = {1, 2, 3, 4};
-
-        {
-            span<int> s = arr;
-            CHECK(s(0) == 1);
-            CHECK_THROW(s(5), fail_fast);
-        }
-
-        {
-            int arr2d[2] = {1, 6};
-            span<int, 2> s = arr2d;
-            CHECK(s(0) == 1);
-            CHECK(s(1) == 6);
-            CHECK_THROW(s(2) ,fail_fast);
-        }
-    }
-
-    TEST(iterator_default_init)
-    {
-        span<int>::iterator it1;
-        span<int>::iterator it2;
-        CHECK(it1 == it2);
-    }
-
-    TEST(const_iterator_default_init)
-    {
-        span<int>::const_iterator it1;
-        span<int>::const_iterator it2;
-        CHECK(it1 == it2);
-    }
-
-    TEST(iterator_conversions)
-    {
-        span<int>::iterator badIt;
-        span<int>::const_iterator badConstIt;
-        CHECK(badIt == badConstIt);
-
-        int a[] = { 1, 2, 3, 4 };
-        span<int> s = a;
-
-        auto it = s.begin();
-        auto cit = s.cbegin();
-
-        CHECK(it == cit);
-        CHECK(cit == it);
-
-        span<int>::const_iterator cit2 = it;
-        CHECK(cit2 == cit);
-
-        span<int>::const_iterator cit3 = it + 4;
-        CHECK(cit3 == s.cend());
-    }
-
-    TEST(iterator_comparisons)
-    {
-        int a[] = { 1, 2, 3, 4 };
-        {
-            span<int> s = a;
-            span<int>::iterator it = s.begin();
-            auto it2 = it + 1;
-            span<int>::const_iterator cit = s.cbegin();
-
-            CHECK(it == cit);
-            CHECK(cit == it);
-            CHECK(it == it);
-            CHECK(cit == cit);
-            CHECK(cit == s.begin());
-            CHECK(s.begin() == cit);
-            CHECK(s.cbegin() == cit);
-            CHECK(it == s.begin());
-            CHECK(s.begin() == it);
-
-            CHECK(it != it2);
-            CHECK(it2 != it);
-            CHECK(it != s.end());
-            CHECK(it2 != s.end());
-            CHECK(s.end() != it);
-            CHECK(it2 != cit);
-            CHECK(cit != it2);
-
-            CHECK(it < it2);
-            CHECK(it <= it2);
-            CHECK(it2 <= s.end());
-            CHECK(it < s.end());
-            CHECK(it <= cit);
-            CHECK(cit <= it);
-            CHECK(cit < it2);
-            CHECK(cit <= it2);
-            CHECK(cit < s.end());
-            CHECK(cit <= s.end());
-
-            CHECK(it2 > it);
-            CHECK(it2 >= it);
-            CHECK(s.end() > it2);
-            CHECK(s.end() >= it2);
-            CHECK(it2 > cit);
-            CHECK(it2 >= cit);
-        }
-    }
-
-    TEST(begin_end)
-    {
-        {
-            int a[] = { 1, 2, 3, 4 };
-            span<int> s = a;
-
-            span<int>::iterator it = s.begin();
-            span<int>::iterator it2 = std::begin(s);
-            CHECK(it == it2);
-
-            it = s.end();
-            it2 = std::end(s);
-            CHECK(it == it2);
-        }
-
-        {
-            int a[] = { 1, 2, 3, 4 };
-            span<int> s = a;
-
-            auto it = s.begin();
-            auto first = it;
-            CHECK(it == first);
-            CHECK(*it == 1);
-
-            auto beyond = s.end();
-            CHECK(it != beyond);
-            CHECK_THROW(*beyond, fail_fast);
-
-            CHECK(beyond - first == 4);
-            CHECK(first - first == 0);
-            CHECK(beyond - beyond == 0);
-            
-            ++it;
-            CHECK(it - first == 1);
-            CHECK(*it == 2);
-            *it = 22;
-            CHECK(*it == 22);
-            CHECK(beyond - it == 3);
-
-            it = first;
-            CHECK(it == first);
-            while (it != s.end())
-            {
-                *it = 5;
-                ++it;
-            }
-
-            CHECK(it == beyond);
-            CHECK(it - beyond == 0);
-
-            for (auto& n : s)
-            {
-                CHECK(n == 5);
-            }
-        }
-    }
-
-    TEST(cbegin_cend)
-    {
-        {
-            int a[] = { 1, 2, 3, 4 };
-            span<int> s = a;
-
-            span<int>::const_iterator cit = s.cbegin();
-            span<int>::const_iterator cit2 = std::cbegin(s);
-            CHECK(cit == cit2);
-
-            cit = s.cend();
-            cit2 = std::cend(s);
-            CHECK(cit == cit2);
-        }
-
-        {
-            int a[] = {1, 2, 3, 4};
-            span<int> s = a;
-
-            auto it = s.cbegin();
-            auto first = it;
-            CHECK(it == first);
-            CHECK(*it == 1);
-
-            auto beyond = s.cend();
-            CHECK(it != beyond);
-            CHECK_THROW(*beyond, fail_fast);
-
-            CHECK(beyond - first == 4);
-            CHECK(first - first == 0);
-            CHECK(beyond - beyond == 0);
-
-            ++it;
-            CHECK(it - first == 1);
-            CHECK(*it == 2);
-            CHECK(beyond - it == 3);
-
-            int last = 0;
-            it = first;
-            CHECK(it == first);
-            while (it != s.cend())
-            {
-                CHECK(*it == last + 1);
-
-                last = *it;
-                ++it;
-            }
-
-            CHECK(it == beyond);
-            CHECK(it - beyond == 0);
-        }
-    }
-
-    TEST(rbegin_rend)
-    {
-        {
-            int a[] = {1, 2, 3, 4};
-            span<int> s = a;
-
-            auto it = s.rbegin();
-            auto first = it;
-            CHECK(it == first);
-            CHECK(*it == 4);
-
-            auto beyond = s.rend();
-            CHECK(it != beyond);
-            CHECK_THROW(*beyond, fail_fast);
-
-            CHECK(beyond - first == 4);
-            CHECK(first - first == 0);
-            CHECK(beyond - beyond == 0);
-
-            ++it;
-            CHECK(it - first == 1);
-            CHECK(*it == 3);
-            *it = 22;
-            CHECK(*it == 22);
-            CHECK(beyond - it == 3);
-
-            it = first;
-            CHECK(it == first);
-            while (it != s.rend())
-            {
-                *it = 5;
-                ++it;
-            }
-
-            CHECK(it == beyond);
-            CHECK(it - beyond == 0);
-
-            for (auto& n : s)
-            {
-                CHECK(n == 5);
-            }
-        }
-    }
-
-    TEST(crbegin_crend)
-    {
-        {
-            int a[] = {1, 2, 3, 4};
-            span<int> s = a;
-
-            auto it = s.crbegin();
-            auto first = it;
-            CHECK(it == first);
-            CHECK(*it == 4);
-
-            auto beyond = s.crend();
-            CHECK(it != beyond);
-            CHECK_THROW(*beyond, fail_fast);
-
-            CHECK(beyond - first == 4);
-            CHECK(first - first == 0);
-            CHECK(beyond - beyond == 0);
-
-            ++it;
-            CHECK(it - first == 1);
-            CHECK(*it == 3);
-            CHECK(beyond - it == 3);
-
-            it = first;
-            CHECK(it == first);
-            int last = 5;
-            while (it != s.crend())
-            {
-                CHECK(*it == last - 1);
-                last = *it;
-
-                ++it;
-            }
-
-            CHECK(it == beyond);
-            CHECK(it - beyond == 0);
-        }
-    }
-
-    TEST(comparison_operators)
-    {
-        {
-            span<int> s1 = nullptr;
-            span<int> s2 = nullptr;
-            CHECK(s1 == s2);
-            CHECK(!(s1 != s2));
-            CHECK(!(s1 < s2));
-            CHECK(s1 <= s2);
-            CHECK(!(s1 > s2));
-            CHECK(s1 >= s2);
-            CHECK(s2 == s1);
-            CHECK(!(s2 != s1));
-            CHECK(!(s2 < s1));
-            CHECK(s2 <= s1);
-            CHECK(!(s2 > s1));
-            CHECK(s2 >= s1);
-        }
-
-        {
-            int arr[] = {2, 1};
-            span<int> s1 = arr;
-            span<int> s2 = arr;
-
-            CHECK(s1 == s2);
-            CHECK(!(s1 != s2));
-            CHECK(!(s1 < s2));
-            CHECK(s1 <= s2);
-            CHECK(!(s1 > s2));
-            CHECK(s1 >= s2);
-            CHECK(s2 == s1);
-            CHECK(!(s2 != s1));
-            CHECK(!(s2 < s1));
-            CHECK(s2 <= s1);
-            CHECK(!(s2 > s1));
-            CHECK(s2 >= s1);
-        }
-
-        {
-            int arr[] = {2, 1}; // bigger
-
-            span<int> s1 = nullptr;
-            span<int> s2 = arr;
-
-            CHECK(s1 != s2);
-            CHECK(s2 != s1);
-            CHECK(!(s1 == s2));
-            CHECK(!(s2 == s1));
-            CHECK(s1 < s2);
-            CHECK(!(s2 < s1));
-            CHECK(s1 <= s2);
-            CHECK(!(s2 <= s1));
-            CHECK(s2 > s1);
-            CHECK(!(s1 > s2));
-            CHECK(s2 >= s1);
-            CHECK(!(s1 >= s2));
-        }
-
-        {
-            int arr1[] = {1, 2};
-            int arr2[] = {1, 2};
-            span<int> s1 = arr1;
-            span<int> s2 = arr2;
-
-            CHECK(s1 == s2);
-            CHECK(!(s1 != s2));
-            CHECK(!(s1 < s2));
-            CHECK(s1 <= s2);
-            CHECK(!(s1 > s2));
-            CHECK(s1 >= s2);
-            CHECK(s2 == s1);
-            CHECK(!(s2 != s1));
-            CHECK(!(s2 < s1));
-            CHECK(s2 <= s1);
-            CHECK(!(s2 > s1));
-            CHECK(s2 >= s1);
-        }
-
-        {
-            int arr[] = {1, 2, 3};
-
-            span<int> s1 = {&arr[0], 2}; // shorter
-            span<int> s2 = arr; // longer
-
-            CHECK(s1 != s2);
-            CHECK(s2 != s1);
-            CHECK(!(s1 == s2));
-            CHECK(!(s2 == s1));
-            CHECK(s1 < s2);
-            CHECK(!(s2 < s1));
-            CHECK(s1 <= s2);
-            CHECK(!(s2 <= s1));
-            CHECK(s2 > s1);
-            CHECK(!(s1 > s2));
-            CHECK(s2 >= s1);
-            CHECK(!(s1 >= s2));
-        }
-
-        {
-            int arr1[] = {1, 2}; // smaller
-            int arr2[] = {2, 1}; // bigger
-
-            span<int> s1 = arr1;
-            span<int> s2 = arr2;
-
-            CHECK(s1 != s2);
-            CHECK(s2 != s1);
-            CHECK(!(s1 == s2));
-            CHECK(!(s2 == s1));
-            CHECK(s1 < s2);
-            CHECK(!(s2 < s1));
-            CHECK(s1 <= s2);
-            CHECK(!(s2 <= s1));
-            CHECK(s2 > s1);
-            CHECK(!(s1 > s2));
-            CHECK(s2 >= s1);
-            CHECK(!(s1 >= s2));
-        }
-    }
-
-    TEST(as_bytes)
-    {
-        int a[] = {1, 2, 3, 4};
-
-        {
-            span<const int> s = a;
-            CHECK(s.length() == 4);
-            span<const byte> bs = as_bytes(s);
-            CHECK(static_cast<const void*>(bs.data()) == static_cast<const void*>(s.data()));
-            CHECK(bs.length() == s.length_bytes());
-        }
-
-        {
-            span<int> s;
-            auto bs = as_bytes(s);
-            CHECK(bs.length() == s.length());
-            CHECK(bs.length() == 0);
-            CHECK(bs.size_bytes() == 0);
-            CHECK(static_cast<const void*>(bs.data()) == static_cast<const void*>(s.data()));
-            CHECK(bs.data() == nullptr);
-        }
-
-        {
-            span<int> s = a;
-            auto bs = as_bytes(s);
-            CHECK(static_cast<const void*>(bs.data()) == static_cast<const void*>(s.data()));
-            CHECK(bs.length() == s.length_bytes());
-        }
-    }
-
-    TEST(as_writeable_bytes)
-    {
-        int a[] = {1, 2, 3, 4};
-
-        {
-#ifdef CONFIRM_COMPILATION_ERRORS
-            // you should not be able to get writeable bytes for const objects
-            span<const int> s = a;
-            CHECK(s.length() == 4);
-            span<const byte> bs = as_writeable_bytes(s);
-            CHECK(static_cast<void*>(bs.data()) == static_cast<void*>(s.data()));
-            CHECK(bs.length() == s.length_bytes());
-#endif
-        }
-
-        {
-            span<int> s;
-            auto bs = as_writeable_bytes(s);
-            CHECK(bs.length() == s.length());
-            CHECK(bs.length() == 0);
-            CHECK(bs.size_bytes() == 0);
-            CHECK(static_cast<void*>(bs.data()) == static_cast<void*>(s.data()));
-            CHECK(bs.data() == nullptr);
-        }
-
-        {
-            span<int> s = a;
-            auto bs = as_writeable_bytes(s);
-            CHECK(static_cast<void*>(bs.data()) == static_cast<void*>(s.data()));
-            CHECK(bs.length() == s.length_bytes());
-        }
-    }
-
-    TEST(fixed_size_conversions)
-    {
-        int arr[] = {1, 2, 3, 4};
-
-        // converting to an span from an equal size array is ok
-        span<int, 4> s4 = arr;
-        CHECK(s4.length() == 4);
-
-        // converting to dynamic_range is always ok
-        {
-            span<int> s = s4;
-            CHECK(s.length() == s4.length());
-            static_cast<void>(s);
-        }
-
-// initialization or assignment to static span that REDUCES size is NOT ok
-#ifdef CONFIRM_COMPILATION_ERRORS
-        {
-            span<int, 2> s = arr;
-        }
-        {
-            span<int, 2> s2 = s4;
-            static_cast<void>(s2);
-        }
-#endif
-
-        // even when done dynamically
-        {
-            span<int> s = arr;
-            auto f = [&]() {
-                span<int, 2> s2 = s;
-                static_cast<void>(s2);
-            };
-            CHECK_THROW(f(), fail_fast);
-        }
-
-        // but doing so explicitly is ok
-
-        // you can convert statically
-        {
-            span<int, 2> s2 = {arr, 2};
-            static_cast<void>(s2);
-        }
-        {
-            span<int, 1> s1 = s4.first<1>();
-            static_cast<void>(s1);
-        }
-
-        // ...or dynamically
-        {
-            // NB: implicit conversion to span<int,1> from span<int>
-            span<int, 1> s1 = s4.first(1);
-            static_cast<void>(s1);
-        }
-
-        // initialization or assignment to static span that requires size INCREASE is not ok.
-        int arr2[2] = {1, 2};
-
-#ifdef CONFIRM_COMPILATION_ERRORS
-        {
-            span<int, 4> s3 = arr2;
-        }
-        {
-            span<int, 2> s2 = arr2;
-            span<int, 4> s4a = s2;
-        }
-#endif
-        {
-            auto f = [&]() {
-                span<int, 4> s4 = {arr2, 2};
-                static_cast<void>(s4);
-            };                     
-            CHECK_THROW(f(), fail_fast);
-        }
-
-        // this should fail - we are trying to assign a small dynamic span to a fixed_size larger one
-        span<int> av = arr2;
-        auto f = [&]() {
-            span<int, 4> s4 = av;
-            static_cast<void>(s4);            
-        };
-        CHECK_THROW(f(), fail_fast);
-    }
-
-    TEST(interop_with_std_regex)
-    {
-        char lat[] = { '1', '2', '3', '4', '5', '6', 'E', 'F', 'G' };
-        span<char> s = lat;
-        auto f_it = s.begin() + 7;
-
-        std::match_results<span<char>::iterator> match;
-
-        std::regex_match(s.begin(), s.end(), match, std::regex(".*"));
-        CHECK(match.ready());
-        CHECK(!match.empty());
-        CHECK(match[0].matched);
-        CHECK(match[0].first == s.begin());
-        CHECK(match[0].second == s.end());
-
-        std::regex_search(s.begin(), s.end(), match, std::regex("F"));
-        CHECK(match.ready());
-        CHECK(!match.empty());
-        CHECK(match[0].matched);
-        CHECK(match[0].first == f_it);
-        CHECK(match[0].second == (f_it + 1));
-    }
-
-    TEST(interop_with_gsl_at)
-    {
-        int arr[5] = {1, 2, 3, 4, 5};
-        span<int> s{arr};
-        CHECK(at(s,0) == 1 && at(s,1) == 2);
+        span<const int> cs{};
+        CHECK((cs.length() == 0 && cs.data() == nullptr));
     }
 }
 
-int main(int, const char* []) { return UnitTest::RunAllTests(); }
+TEST_CASE("size_optimization")
+{
+    {
+        span<int> s;
+        CHECK(sizeof(s) == sizeof(int*) + sizeof(ptrdiff_t));
+    }
+
+    {
+        span<int, 0> s;
+        CHECK(sizeof(s) == sizeof(int*));
+    }
+}
+
+TEST_CASE("from_nullptr_constructor")
+{
+    {
+        span<int> s = nullptr;
+        CHECK((s.length() == 0 && s.data() == nullptr));
+
+        span<const int> cs = nullptr;
+        CHECK((cs.length() == 0 && cs.data() == nullptr));
+    }
+
+    {
+        span<int, 0> s = nullptr;
+        CHECK((s.length() == 0 && s.data() == nullptr));
+
+        span<const int, 0> cs = nullptr;
+        CHECK((cs.length() == 0 && cs.data() == nullptr));
+    }
+
+    {
+#ifdef CONFIRM_COMPILATION_ERRORS
+        span<int, 1> s = nullptr;
+        CHECK((s.length() == 1 && s.data() == nullptr)); // explains why it can't compile
+#endif
+    }
+
+    {
+        span<int> s{nullptr};
+        CHECK((s.length() == 0 && s.data() == nullptr));
+
+        span<const int> cs{nullptr};
+        CHECK((cs.length() == 0 && cs.data() == nullptr));
+    }
+
+    {
+        span<int*> s{nullptr};
+        CHECK((s.length() == 0 && s.data() == nullptr));
+
+        span<const int*> cs{nullptr};
+        CHECK((cs.length() == 0 && cs.data() == nullptr));
+    }
+}
+
+TEST_CASE("from_nullptr_length_constructor")
+{
+    {
+        span<int> s{nullptr, static_cast<span<int>::index_type>(0)};
+        CHECK((s.length() == 0 && s.data() == nullptr));
+
+        span<const int> cs{nullptr, static_cast<span<int>::index_type>(0)};
+        CHECK((cs.length() == 0 && cs.data() == nullptr));
+    }
+
+    {
+        span<int, 0> s{nullptr, static_cast<span<int>::index_type>(0)};
+        CHECK((s.length() == 0 && s.data() == nullptr));
+
+        span<const int, 0> cs{nullptr, static_cast<span<int>::index_type>(0)};
+        CHECK((cs.length() == 0 && cs.data() == nullptr));
+    }
+
+    {
+        auto workaround_macro = []() {
+            span<int, 1> s{nullptr, static_cast<span<int>::index_type>(0)};
+        };
+        CHECK_THROWS_AS(workaround_macro(), fail_fast);
+    }
+
+    {
+        auto workaround_macro = []() { span<int> s{nullptr, 1}; };
+        CHECK_THROWS_AS(workaround_macro(), fail_fast);
+
+        auto const_workaround_macro = []() { span<const int> cs{nullptr, 1}; };
+        CHECK_THROWS_AS(const_workaround_macro(), fail_fast);
+    }
+
+    {
+        auto workaround_macro = []() { span<int, 0> s{nullptr, 1}; };
+        CHECK_THROWS_AS(workaround_macro(), fail_fast);
+
+        auto const_workaround_macro = []() { span<const int, 0> s{nullptr, 1}; };
+        CHECK_THROWS_AS(const_workaround_macro(), fail_fast);
+    }
+
+    {
+        span<int*> s{nullptr, static_cast<span<int>::index_type>(0)};
+        CHECK((s.length() == 0 && s.data() == nullptr));
+
+        span<const int*> cs{nullptr, static_cast<span<int>::index_type>(0)};
+        CHECK((cs.length() == 0 && cs.data() == nullptr));
+    }
+}
+
+TEST_CASE("from_pointer_length_constructor")
+{
+    int arr[4] = {1, 2, 3, 4};
+
+    {
+        span<int> s{&arr[0], 2};
+        CHECK((s.length() == 2 && s.data() == &arr[0]));
+        CHECK((s[0] == 1 && s[1] == 2));
+    }
+
+    {
+        span<int, 2> s{&arr[0], 2};
+        CHECK((s.length() == 2 && s.data() == &arr[0]));
+        CHECK((s[0] == 1 && s[1] == 2));
+    }
+
+    {
+        int* p = nullptr;
+        span<int> s{p, static_cast<span<int>::index_type>(0)};
+        CHECK((s.length() == 0 && s.data() == nullptr));
+    }
+
+    {
+        int* p = nullptr;
+        auto workaround_macro = [=]() { span<int> s{p, 2}; };
+        CHECK_THROWS_AS(workaround_macro(), fail_fast);
+    }
+
+    {
+        auto s = make_span(&arr[0], 2);
+        CHECK((s.length() == 2 && s.data() == &arr[0]));
+        CHECK((s[0] == 1 && s[1] == 2));
+    }
+
+    {
+        int* p = nullptr;
+        auto s = make_span(p, static_cast<span<int>::index_type>(0));
+        CHECK((s.length() == 0 && s.data() == nullptr));
+    }
+
+    {
+        int* p = nullptr;
+        auto workaround_macro = [=]() { make_span(p, 2); };
+        CHECK_THROWS_AS(workaround_macro(), fail_fast);
+    }
+}
+
+TEST_CASE("from_pointer_pointer_constructor")
+{
+    int arr[4] = {1, 2, 3, 4};
+
+    {
+        span<int> s{&arr[0], &arr[2]};
+        CHECK((s.length() == 2 && s.data() == &arr[0]));
+        CHECK((s[0] == 1 && s[1] == 2));
+    }
+
+    {
+        span<int, 2> s{&arr[0], &arr[2]};
+        CHECK((s.length() == 2 && s.data() == &arr[0]));
+        CHECK((s[0] == 1 && s[1] == 2));
+    }
+
+    {
+        span<int> s{&arr[0], &arr[0]};
+        CHECK((s.length() == 0 && s.data() == &arr[0]));
+    }
+
+    {
+        span<int, 0> s{&arr[0], &arr[0]};
+        CHECK((s.length() == 0 && s.data() == &arr[0]));
+    }
+
+    // this will fail the std::distance() precondition, which asserts on MSVC debug builds
+    //{
+    //    auto workaround_macro = [&]() { span<int> s{&arr[1], &arr[0]}; };
+    //    CHECK_THROWS_AS(workaround_macro(), fail_fast);
+    //}
+
+    // this will fail the std::distance() precondition, which asserts on MSVC debug builds
+    //{
+    //    int* p = nullptr;
+    //    auto workaround_macro = [&]() { span<int> s{&arr[0], p}; };
+    //    CHECK_THROWS_AS(workaround_macro(), fail_fast);
+    //}
+
+    {
+        int* p = nullptr;
+        span<int> s{p, p};
+        CHECK((s.length() == 0 && s.data() == nullptr));
+    }
+
+    {
+        int* p = nullptr;
+        span<int, 0> s{p, p};
+        CHECK((s.length() == 0 && s.data() == nullptr));
+    }
+
+    // this will fail the std::distance() precondition, which asserts on MSVC debug builds
+    //{
+    //    int* p = nullptr;
+    //    auto workaround_macro = [&]() { span<int> s{&arr[0], p}; };
+    //    CHECK_THROWS_AS(workaround_macro(), fail_fast);
+    //}
+
+    {
+        auto s = make_span(&arr[0], &arr[2]);
+        CHECK((s.length() == 2 && s.data() == &arr[0]));
+        CHECK((s[0] == 1 && s[1] == 2));
+    }
+
+    {
+        auto s = make_span(&arr[0], &arr[0]);
+        CHECK((s.length() == 0 && s.data() == &arr[0]));
+    }
+
+    {
+        int* p = nullptr;
+        auto s = make_span(p, p);
+        CHECK((s.length() == 0 && s.data() == nullptr));
+    }
+}
+
+TEST_CASE("from_array_constructor")
+{
+    int arr[5] = {1, 2, 3, 4, 5};
+
+    {
+        span<int> s{arr};
+        CHECK((s.length() == 5 && s.data() == &arr[0]));
+    }
+
+    {
+        span<int, 5> s{arr};
+        CHECK((s.length() == 5 && s.data() == &arr[0]));
+    }
+
+    int arr2d[2][3] = {1, 2, 3, 4, 5, 6};
+
+#ifdef CONFIRM_COMPILATION_ERRORS
+    {
+        span<int, 6> s{arr};
+    }
+
+    {
+        span<int, 0> s{arr};
+        CHECK((s.length() == 0 && s.data() == &arr[0]));
+    }
+
+    {
+        span<int> s{arr2d};
+        CHECK((s.length() == 6 && s.data() == &arr2d[0][0]));
+        CHECK((s[0] == 1 && s[5] == 6));
+    }
+
+    {
+        span<int, 0> s{arr2d};
+        CHECK((s.length() == 0 && s.data() == &arr2d[0][0]));
+    }
+
+    {
+        span<int, 6> s{arr2d};
+    }
+#endif
+    {
+        span<int[3]> s{&(arr2d[0]), 1};
+        CHECK((s.length() == 1 && s.data() == &arr2d[0]));
+    }
+
+    int arr3d[2][3][2] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12};
+
+#ifdef CONFIRM_COMPILATION_ERRORS
+    {
+        span<int> s{arr3d};
+        CHECK((s.length() == 12 && s.data() == &arr3d[0][0][0]));
+        CHECK((s[0] == 1 && s[11] == 12));
+    }
+
+    {
+        span<int, 0> s{arr3d};
+        CHECK((s.length() == 0 && s.data() == &arr3d[0][0][0]));
+    }
+
+    {
+        span<int, 11> s{arr3d};
+    }
+
+    {
+        span<int, 12> s{arr3d};
+        CHECK((s.length() == 12 && s.data() == &arr3d[0][0][0]));
+        CHECK((s[0] == 1 && s[5] == 6));
+    }
+#endif
+    {
+        span<int[3][2]> s{&arr3d[0], 1};
+        CHECK((s.length() == 1 && s.data() == &arr3d[0]));
+    }
+
+    {
+        auto s = make_span(arr);
+        CHECK((s.length() == 5 && s.data() == &arr[0]));
+    }
+
+    {
+        auto s = make_span(&(arr2d[0]), 1);
+        CHECK((s.length() == 1 && s.data() == &arr2d[0]));
+    }
+
+    {
+        auto s = make_span(&arr3d[0], 1);
+        CHECK((s.length() == 1 && s.data() == &arr3d[0]));
+    }
+}
+
+TEST_CASE("from_dynamic_array_constructor")
+{
+    double(*arr)[3][4] = new double[100][3][4];
+
+    {
+        span<double> s(&arr[0][0][0], 10);
+        CHECK((s.length() == 10 && s.data() == &arr[0][0][0]));
+    }
+
+    {
+        auto s = make_span(&arr[0][0][0], 10);
+        CHECK((s.length() == 10 && s.data() == &arr[0][0][0]));
+    }
+
+    delete[] arr;
+}
+
+TEST_CASE("from_std_array_constructor")
+{
+    std::array<int, 4> arr = {1, 2, 3, 4};
+
+    {
+        span<int> s{arr};
+        CHECK((s.size() == narrow_cast<ptrdiff_t>(arr.size()) && s.data() == arr.data()));
+
+        span<const int> cs{arr};
+        CHECK((cs.size() == narrow_cast<ptrdiff_t>(arr.size()) && cs.data() == arr.data()));
+    }
+
+    {
+        span<int, 4> s{arr};
+        CHECK((s.size() == narrow_cast<ptrdiff_t>(arr.size()) && s.data() == arr.data()));
+
+        span<const int, 4> cs{arr};
+        CHECK((cs.size() == narrow_cast<ptrdiff_t>(arr.size()) && cs.data() == arr.data()));
+    }
+
+#ifdef CONFIRM_COMPILATION_ERRORS
+    {
+        span<int, 2> s{arr};
+        CHECK((s.size() == 2 && s.data() == arr.data()));
+
+        span<const int, 2> cs{arr};
+        CHECK((cs.size() == 2 && cs.data() == arr.data()));
+    }
+
+    {
+        span<int, 0> s{arr};
+        CHECK((s.size() == 0 && s.data() == arr.data()));
+
+        span<const int, 0> cs{arr};
+        CHECK((cs.size() == 0 && cs.data() == arr.data()));
+    }
+
+    {
+        span<int, 5> s{arr};
+    }
+
+    {
+        auto get_an_array = []() -> std::array<int, 4> { return {1, 2, 3, 4}; };
+        auto take_a_span = [](span<int> s) { static_cast<void>(s); };
+        // try to take a temporary std::array
+        take_a_span(get_an_array());
+    }
+#endif
+
+    {
+        auto get_an_array = []() -> std::array<int, 4> { return {1, 2, 3, 4}; };
+        auto take_a_span = [](span<const int> s) { static_cast<void>(s); };
+        // try to take a temporary std::array
+        take_a_span(get_an_array());
+    }
+
+    {
+        auto s = make_span(arr);
+        CHECK((s.size() == narrow_cast<ptrdiff_t>(arr.size()) && s.data() == arr.data()));
+    }
+}
+
+TEST_CASE("from_const_std_array_constructor")
+{
+    const std::array<int, 4> arr = {1, 2, 3, 4};
+
+    {
+        span<const int> s{arr};
+        CHECK((s.size() == narrow_cast<ptrdiff_t>(arr.size()) && s.data() == arr.data()));
+    }
+
+    {
+        span<const int, 4> s{arr};
+        CHECK((s.size() == narrow_cast<ptrdiff_t>(arr.size()) && s.data() == arr.data()));
+    }
+
+#ifdef CONFIRM_COMPILATION_ERRORS
+    {
+        span<const int, 2> s{arr};
+        CHECK((s.size() == 2 && s.data() == arr.data()));
+    }
+
+    {
+        span<const int, 0> s{arr};
+        CHECK((s.size() == 0 && s.data() == arr.data()));
+    }
+
+    {
+        span<const int, 5> s{arr};
+    }
+#endif
+
+    {
+        auto get_an_array = []() -> const std::array<int, 4> { return {1, 2, 3, 4}; };
+        auto take_a_span = [](span<const int> s) { static_cast<void>(s); };
+        // try to take a temporary std::array
+        take_a_span(get_an_array());
+    }
+
+    {
+        auto s = make_span(arr);
+        CHECK((s.size() == narrow_cast<ptrdiff_t>(arr.size()) && s.data() == arr.data()));
+    }
+}
+
+TEST_CASE("from_std_array_const_constructor")
+{
+    std::array<const int, 4> arr = {1, 2, 3, 4};
+
+    {
+        span<const int> s{arr};
+        CHECK((s.size() == narrow_cast<ptrdiff_t>(arr.size()) && s.data() == arr.data()));
+    }
+
+    {
+        span<const int, 4> s{arr};
+        CHECK((s.size() == narrow_cast<ptrdiff_t>(arr.size()) && s.data() == arr.data()));
+    }
+
+#ifdef CONFIRM_COMPILATION_ERRORS
+    {
+        span<const int, 2> s{arr};
+        CHECK((s.size() == 2 && s.data() == arr.data()));
+    }
+
+    {
+        span<const int, 0> s{arr};
+        CHECK((s.size() == 0 && s.data() == arr.data()));
+    }
+
+    {
+        span<const int, 5> s{arr};
+    }
+
+    {
+        span<int, 4> s{arr};
+    }
+#endif
+
+    {
+        auto s = make_span(arr);
+        CHECK((s.size() == narrow_cast<ptrdiff_t>(arr.size()) && s.data() == arr.data()));
+    }
+}
+
+TEST_CASE("from_unique_pointer_construction")
+{
+    {
+        auto ptr = std::make_unique<int>(4);
+
+        {
+            span<int> s{ptr};
+            CHECK((s.length() == 1 && s.data() == ptr.get()));
+            CHECK(s[0] == 4);
+        }
+
+        {
+            auto s = make_span(ptr);
+            CHECK((s.length() == 1 && s.data() == ptr.get()));
+            CHECK(s[0] == 4);
+        }
+    }
+
+    {
+        auto ptr = std::unique_ptr<int>{nullptr};
+
+        {
+            span<int> s{ptr};
+            CHECK((s.length() == 0 && s.data() == nullptr));
+        }
+
+        {
+            auto s = make_span(ptr);
+            CHECK((s.length() == 0 && s.data() == nullptr));
+        }
+    }
+
+    {
+        auto arr = std::make_unique<int[]>(4);
+
+        for (auto i = 0U; i < 4; i++) arr[i] = gsl::narrow_cast<int>(i + 1);
+
+        {
+            span<int> s{arr, 4};
+            CHECK((s.length() == 4 && s.data() == arr.get()));
+            CHECK((s[0] == 1 && s[1] == 2));
+        }
+
+        {
+            auto s = make_span(arr, 4);
+            CHECK((s.length() == 4 && s.data() == arr.get()));
+            CHECK((s[0] == 1 && s[1] == 2));
+        }
+    }
+
+    {
+        auto arr = std::unique_ptr<int[]>{nullptr};
+
+        {
+            span<int> s{arr, 0};
+            CHECK((s.length() == 0 && s.data() == nullptr));
+        }
+
+        {
+            auto s = make_span(arr, 0);
+            CHECK((s.length() == 0 && s.data() == nullptr));
+        }
+    }
+}
+
+TEST_CASE("from_shared_pointer_construction")
+{
+    {
+        auto ptr = std::make_shared<int>(4);
+
+        {
+            span<int> s{ptr};
+            CHECK((s.length() == 1 && s.data() == ptr.get()));
+            CHECK((s[0] == 4));
+        }
+
+        {
+            auto s = make_span(ptr);
+            CHECK((s.length() == 1 && s.data() == ptr.get()));
+            CHECK((s[0] == 4));
+        }
+    }
+
+    {
+        auto ptr = std::shared_ptr<int>{nullptr};
+
+        {
+            span<int> s{ptr};
+            CHECK((s.length() == 0 && s.data() == nullptr));
+        }
+
+        {
+            auto s = make_span(ptr);
+            CHECK((s.length() == 0 && s.data() == nullptr));
+        }
+    }
+}
+
+TEST_CASE("from_container_constructor")
+{
+    std::vector<int> v = {1, 2, 3};
+    const std::vector<int> cv = v;
+
+    {
+        span<int> s{v};
+        CHECK((s.size() == narrow_cast<std::ptrdiff_t>(v.size()) && s.data() == v.data()));
+
+        span<const int> cs{v};
+        CHECK((cs.size() == narrow_cast<std::ptrdiff_t>(v.size()) && cs.data() == v.data()));
+    }
+
+    std::string str = "hello";
+    const std::string cstr = "hello";
+
+    {
+#ifdef CONFIRM_COMPILATION_ERRORS
+        span<char> s{str};
+        CHECK((s.size() == narrow_cast<std::ptrdiff_t>(str.size()) && s.data() == str.data()));
+#endif
+        span<const char> cs{str};
+        CHECK((cs.size() == narrow_cast<std::ptrdiff_t>(str.size()) && cs.data() == str.data()));
+    }
+
+    {
+#ifdef CONFIRM_COMPILATION_ERRORS
+        span<char> s{cstr};
+#endif
+        span<const char> cs{cstr};
+        CHECK((cs.size() == narrow_cast<std::ptrdiff_t>(cstr.size()) &&
+              cs.data() == cstr.data()));
+    }
+
+    {
+#ifdef CONFIRM_COMPILATION_ERRORS
+        auto get_temp_vector = []() -> std::vector<int> { return {}; };
+        auto use_span = [](span<int> s) { static_cast<void>(s); };
+        use_span(get_temp_vector());
+#endif
+    }
+
+    {
+        auto get_temp_vector = []() -> std::vector<int> { return {}; };
+        auto use_span = [](span<const int> s) { static_cast<void>(s); };
+        use_span(get_temp_vector());
+    }
+
+    {
+#ifdef CONFIRM_COMPILATION_ERRORS
+        auto get_temp_string = []() -> std::string { return {}; };
+        auto use_span = [](span<char> s) { static_cast<void>(s); };
+        use_span(get_temp_string());
+#endif
+    }
+
+    {
+        auto get_temp_string = []() -> std::string { return {}; };
+        auto use_span = [](span<const char> s) { static_cast<void>(s); };
+        use_span(get_temp_string());
+    }
+
+    {
+#ifdef CONFIRM_COMPILATION_ERRORS
+        auto get_temp_vector = []() -> const std::vector<int> { return {}; };
+        auto use_span = [](span<const char> s) { static_cast<void>(s); };
+        use_span(get_temp_vector());
+#endif
+    }
+
+    {
+        auto get_temp_string = []() -> const std::string { return {}; };
+        auto use_span = [](span<const char> s) { static_cast<void>(s); };
+        use_span(get_temp_string());
+    }
+
+    {
+#ifdef CONFIRM_COMPILATION_ERRORS
+        std::map<int, int> m;
+        span<int> s{m};
+#endif
+    }
+
+    {
+        auto s = make_span(v);
+        CHECK((s.size() == narrow_cast<std::ptrdiff_t>(v.size()) && s.data() == v.data()));
+
+        auto cs = make_span(cv);
+        CHECK((cs.size() == narrow_cast<std::ptrdiff_t>(cv.size()) && cs.data() == cv.data()));
+    }
+}
+
+TEST_CASE("from_convertible_span_constructor")
+{
+    {
+        span<DerivedClass> avd;
+        span<const DerivedClass> avcd = avd;
+        static_cast<void>(avcd);
+    }
+
+    {
+    #ifdef CONFIRM_COMPILATION_ERRORS
+        span<DerivedClass> avd;
+        span<BaseClass> avb = avd;
+        static_cast<void>(avb);
+    #endif
+    }
+
+    #ifdef CONFIRM_COMPILATION_ERRORS
+    {
+        span<int> s;
+        span<unsigned int> s2 = s;
+        static_cast<void>(s2);
+    }
+
+    {
+        span<int> s;
+        span<const unsigned int> s2 = s;
+        static_cast<void>(s2);
+    }
+
+    {
+        span<int> s;
+        span<short> s2 = s;
+        static_cast<void>(s2);
+    }
+    #endif
+}
+
+TEST_CASE("copy_move_and_assignment")
+{
+    span<int> s1;
+    CHECK(s1.empty());
+
+    int arr[] = {3, 4, 5};
+
+    span<const int> s2 = arr;
+    CHECK((s2.length() == 3 && s2.data() == &arr[0]));
+
+    s2 = s1;
+    CHECK(s2.empty());
+
+    auto get_temp_span = [&]() -> span<int> { return {&arr[1], 2}; };
+    auto use_span = [&](span<const int> s) { CHECK((s.length() == 2 && s.data() == &arr[1])); };
+    use_span(get_temp_span());
+
+    s1 = get_temp_span();
+    CHECK((s1.length() == 2 && s1.data() == &arr[1]));
+}
+
+TEST_CASE("first")
+{
+    int arr[5] = {1, 2, 3, 4, 5};
+
+    {
+        span<int, 5> av = arr;
+        CHECK(av.first<2>().length() == 2);
+        CHECK(av.first(2).length() == 2);
+    }
+
+    {
+        span<int, 5> av = arr;
+        CHECK(av.first<0>().length() == 0);
+        CHECK(av.first(0).length() == 0);
+    }
+
+    {
+        span<int, 5> av = arr;
+        CHECK(av.first<5>().length() == 5);
+        CHECK(av.first(5).length() == 5);
+    }
+
+    {
+        span<int, 5> av = arr;
+#ifdef CONFIRM_COMPILATION_ERRORS
+        CHECK(av.first<6>().length() == 6);
+        CHECK(av.first<-1>().length() == -1);
+#endif
+        CHECK_THROWS_AS(av.first(6).length(), fail_fast);
+    }
+
+    {
+        span<int> av;
+        CHECK(av.first<0>().length() == 0);
+        CHECK(av.first(0).length() == 0);
+    }
+}
+
+TEST_CASE("last")
+{
+    int arr[5] = {1, 2, 3, 4, 5};
+
+    {
+        span<int, 5> av = arr;
+        CHECK(av.last<2>().length() == 2);
+        CHECK(av.last(2).length() == 2);
+    }
+
+    {
+        span<int, 5> av = arr;
+        CHECK(av.last<0>().length() == 0);
+        CHECK(av.last(0).length() == 0);
+    }
+
+    {
+        span<int, 5> av = arr;
+        CHECK(av.last<5>().length() == 5);
+        CHECK(av.last(5).length() == 5);
+    }
+
+    {
+        span<int, 5> av = arr;
+#ifdef CONFIRM_COMPILATION_ERRORS
+        CHECK(av.last<6>().length() == 6);
+#endif
+        CHECK_THROWS_AS(av.last(6).length(), fail_fast);
+    }
+
+    {
+        span<int> av;
+        CHECK(av.last<0>().length() == 0);
+        CHECK(av.last(0).length() == 0);
+    }
+}
+
+TEST_CASE("subspan")
+{
+    int arr[5] = {1, 2, 3, 4, 5};
+
+    {
+        span<int, 5> av = arr;
+        CHECK((av.subspan<2, 2>().length() == 2));
+        CHECK(av.subspan(2, 2).length() == 2);
+        CHECK(av.subspan(2, 3).length() == 3);
+    }
+
+    {
+        span<int, 5> av = arr;
+        CHECK((av.subspan<0, 0>().length() == 0));
+        CHECK(av.subspan(0, 0).length() == 0);
+    }
+
+    {
+        span<int, 5> av = arr;
+        CHECK((av.subspan<0, 5>().length() == 5));
+        CHECK(av.subspan(0, 5).length() == 5);
+        CHECK_THROWS_AS(av.subspan(0, 6).length(), fail_fast);
+        CHECK_THROWS_AS(av.subspan(1, 5).length(), fail_fast);
+    }
+
+    {
+        span<int, 5> av = arr;
+        CHECK((av.subspan<4, 0>().length() == 0));
+        CHECK(av.subspan(4, 0).length() == 0);
+        CHECK(av.subspan(5, 0).length() == 0);
+        CHECK_THROWS_AS(av.subspan(6, 0).length(), fail_fast);
+    }
+
+    {
+        span<int> av;
+        CHECK((av.subspan<0, 0>().length() == 0));
+        CHECK(av.subspan(0, 0).length() == 0);
+        CHECK_THROWS_AS((av.subspan<1, 0>().length()), fail_fast);
+    }
+
+    {
+        span<int> av;
+        CHECK(av.subspan(0).length() == 0);
+        CHECK_THROWS_AS(av.subspan(1).length(), fail_fast);
+    }
+
+    {
+        span<int> av = arr;
+        CHECK(av.subspan(0).length() == 5);
+        CHECK(av.subspan(1).length() == 4);
+        CHECK(av.subspan(4).length() == 1);
+        CHECK(av.subspan(5).length() == 0);
+        CHECK_THROWS_AS(av.subspan(6).length(), fail_fast);
+        const auto av2 = av.subspan(1);
+        for (int i = 0; i < 4; ++i) CHECK(av2[i] == i + 2);
+    }
+
+    {
+        span<int, 5> av = arr;
+        CHECK(av.subspan(0).length() == 5);
+        CHECK(av.subspan(1).length() == 4);
+        CHECK(av.subspan(4).length() == 1);
+        CHECK(av.subspan(5).length() == 0);
+        CHECK_THROWS_AS(av.subspan(6).length(), fail_fast);
+        const auto av2 = av.subspan(1);
+        for (int i = 0; i < 4; ++i) CHECK(av2[i] == i + 2);
+    }
+}
+
+TEST_CASE("at_call")
+{
+    int arr[4] = {1, 2, 3, 4};
+
+    {
+        span<int> s = arr;
+        CHECK(s.at(0) == 1);
+        CHECK_THROWS_AS(s.at(5), fail_fast);
+    }
+
+    {
+        int arr2d[2] = {1, 6};
+        span<int, 2> s = arr2d;
+        CHECK(s.at(0) == 1);
+        CHECK(s.at(1) == 6);
+        CHECK_THROWS_AS(s.at(2), fail_fast);
+    }
+}
+
+TEST_CASE("operator_function_call")
+{
+    int arr[4] = {1, 2, 3, 4};
+
+    {
+        span<int> s = arr;
+        CHECK(s(0) == 1);
+        CHECK_THROWS_AS(s(5), fail_fast);
+    }
+
+    {
+        int arr2d[2] = {1, 6};
+        span<int, 2> s = arr2d;
+        CHECK(s(0) == 1);
+        CHECK(s(1) == 6);
+        CHECK_THROWS_AS(s(2), fail_fast);
+    }
+}
+
+TEST_CASE("iterator_default_init")
+{
+    span<int>::iterator it1;
+    span<int>::iterator it2;
+    CHECK(it1 == it2);
+}
+
+TEST_CASE("const_iterator_default_init")
+{
+    span<int>::const_iterator it1;
+    span<int>::const_iterator it2;
+    CHECK(it1 == it2);
+}
+
+TEST_CASE("iterator_conversions")
+{
+    span<int>::iterator badIt;
+    span<int>::const_iterator badConstIt;
+    CHECK(badIt == badConstIt);
+
+    int a[] = {1, 2, 3, 4};
+    span<int> s = a;
+
+    auto it = s.begin();
+    auto cit = s.cbegin();
+
+    CHECK(it == cit);
+    CHECK(cit == it);
+
+    span<int>::const_iterator cit2 = it;
+    CHECK(cit2 == cit);
+
+    span<int>::const_iterator cit3 = it + 4;
+    CHECK(cit3 == s.cend());
+}
+
+TEST_CASE("iterator_comparisons")
+{
+    int a[] = {1, 2, 3, 4};
+    {
+        span<int> s = a;
+        span<int>::iterator it = s.begin();
+        auto it2 = it + 1;
+        span<int>::const_iterator cit = s.cbegin();
+
+        CHECK(it == cit);
+        CHECK(cit == it);
+        CHECK(it == it);
+        CHECK(cit == cit);
+        CHECK(cit == s.begin());
+        CHECK(s.begin() == cit);
+        CHECK(s.cbegin() == cit);
+        CHECK(it == s.begin());
+        CHECK(s.begin() == it);
+
+        CHECK(it != it2);
+        CHECK(it2 != it);
+        CHECK(it != s.end());
+        CHECK(it2 != s.end());
+        CHECK(s.end() != it);
+        CHECK(it2 != cit);
+        CHECK(cit != it2);
+
+        CHECK(it < it2);
+        CHECK(it <= it2);
+        CHECK(it2 <= s.end());
+        CHECK(it < s.end());
+        CHECK(it <= cit);
+        CHECK(cit <= it);
+        CHECK(cit < it2);
+        CHECK(cit <= it2);
+        CHECK(cit < s.end());
+        CHECK(cit <= s.end());
+
+        CHECK(it2 > it);
+        CHECK(it2 >= it);
+        CHECK(s.end() > it2);
+        CHECK(s.end() >= it2);
+        CHECK(it2 > cit);
+        CHECK(it2 >= cit);
+    }
+}
+
+TEST_CASE("begin_end")
+{
+    {
+        int a[] = {1, 2, 3, 4};
+        span<int> s = a;
+
+        span<int>::iterator it = s.begin();
+        span<int>::iterator it2 = std::begin(s);
+        CHECK(it == it2);
+
+        it = s.end();
+        it2 = std::end(s);
+        CHECK(it == it2);
+    }
+
+    {
+        int a[] = {1, 2, 3, 4};
+        span<int> s = a;
+
+        auto it = s.begin();
+        auto first = it;
+        CHECK(it == first);
+        CHECK(*it == 1);
+
+        auto beyond = s.end();
+        CHECK(it != beyond);
+        CHECK_THROWS_AS(*beyond, fail_fast);
+
+        CHECK(beyond - first == 4);
+        CHECK(first - first == 0);
+        CHECK(beyond - beyond == 0);
+
+        ++it;
+        CHECK(it - first == 1);
+        CHECK(*it == 2);
+        *it = 22;
+        CHECK(*it == 22);
+        CHECK(beyond - it == 3);
+
+        it = first;
+        CHECK(it == first);
+        while (it != s.end()) {
+            *it = 5;
+            ++it;
+        }
+
+        CHECK(it == beyond);
+        CHECK(it - beyond == 0);
+
+        for (const auto& n : s) {
+            CHECK(n == 5);
+        }
+    }
+}
+
+TEST_CASE("cbegin_cend")
+{
+    {
+        int a[] = {1, 2, 3, 4};
+        span<int> s = a;
+
+        span<int>::const_iterator cit = s.cbegin();
+        span<int>::const_iterator cit2 = std::cbegin(s);
+        CHECK(cit == cit2);
+
+        cit = s.cend();
+        cit2 = std::cend(s);
+        CHECK(cit == cit2);
+    }
+
+    {
+        int a[] = {1, 2, 3, 4};
+        span<int> s = a;
+
+        auto it = s.cbegin();
+        auto first = it;
+        CHECK(it == first);
+        CHECK(*it == 1);
+
+        auto beyond = s.cend();
+        CHECK(it != beyond);
+        CHECK_THROWS_AS(*beyond, fail_fast);
+
+        CHECK(beyond - first == 4);
+        CHECK(first - first == 0);
+        CHECK(beyond - beyond == 0);
+
+        ++it;
+        CHECK(it - first == 1);
+        CHECK(*it == 2);
+        CHECK(beyond - it == 3);
+
+        int last = 0;
+        it = first;
+        CHECK(it == first);
+        while (it != s.cend()) {
+            CHECK(*it == last + 1);
+
+            last = *it;
+            ++it;
+        }
+
+        CHECK(it == beyond);
+        CHECK(it - beyond == 0);
+    }
+}
+
+TEST_CASE("rbegin_rend")
+{
+    {
+        int a[] = {1, 2, 3, 4};
+        span<int> s = a;
+
+        auto it = s.rbegin();
+        auto first = it;
+        CHECK(it == first);
+        CHECK(*it == 4);
+
+        auto beyond = s.rend();
+        CHECK(it != beyond);
+        CHECK_THROWS_AS(*beyond, fail_fast);
+
+        CHECK(beyond - first == 4);
+        CHECK(first - first == 0);
+        CHECK(beyond - beyond == 0);
+
+        ++it;
+        CHECK(it - first == 1);
+        CHECK(*it == 3);
+        *it = 22;
+        CHECK(*it == 22);
+        CHECK(beyond - it == 3);
+
+        it = first;
+        CHECK(it == first);
+        while (it != s.rend()) {
+            *it = 5;
+            ++it;
+        }
+
+        CHECK(it == beyond);
+        CHECK(it - beyond == 0);
+
+        for (const auto& n : s) {
+            CHECK(n == 5);
+        }
+    }
+}
+
+TEST_CASE("crbegin_crend")
+{
+    {
+        int a[] = {1, 2, 3, 4};
+        span<int> s = a;
+
+        auto it = s.crbegin();
+        auto first = it;
+        CHECK(it == first);
+        CHECK(*it == 4);
+
+        auto beyond = s.crend();
+        CHECK(it != beyond);
+        CHECK_THROWS_AS(*beyond, fail_fast);
+
+        CHECK(beyond - first == 4);
+        CHECK(first - first == 0);
+        CHECK(beyond - beyond == 0);
+
+        ++it;
+        CHECK(it - first == 1);
+        CHECK(*it == 3);
+        CHECK(beyond - it == 3);
+
+        it = first;
+        CHECK(it == first);
+        int last = 5;
+        while (it != s.crend()) {
+            CHECK(*it == last - 1);
+            last = *it;
+
+            ++it;
+        }
+
+        CHECK(it == beyond);
+        CHECK(it - beyond == 0);
+    }
+}
+
+TEST_CASE("comparison_operators")
+{
+    {
+        span<int> s1 = nullptr;
+        span<int> s2 = nullptr;
+        CHECK(s1 == s2);
+        CHECK(!(s1 != s2));
+        CHECK(!(s1 < s2));
+        CHECK(s1 <= s2);
+        CHECK(!(s1 > s2));
+        CHECK(s1 >= s2);
+        CHECK(s2 == s1);
+        CHECK(!(s2 != s1));
+        CHECK(!(s2 < s1));
+        CHECK(s2 <= s1);
+        CHECK(!(s2 > s1));
+        CHECK(s2 >= s1);
+    }
+
+    {
+        int arr[] = {2, 1};
+        span<int> s1 = arr;
+        span<int> s2 = arr;
+
+        CHECK(s1 == s2);
+        CHECK(!(s1 != s2));
+        CHECK(!(s1 < s2));
+        CHECK(s1 <= s2);
+        CHECK(!(s1 > s2));
+        CHECK(s1 >= s2);
+        CHECK(s2 == s1);
+        CHECK(!(s2 != s1));
+        CHECK(!(s2 < s1));
+        CHECK(s2 <= s1);
+        CHECK(!(s2 > s1));
+        CHECK(s2 >= s1);
+    }
+
+    {
+        int arr[] = {2, 1}; // bigger
+
+        span<int> s1 = nullptr;
+        span<int> s2 = arr;
+
+        CHECK(s1 != s2);
+        CHECK(s2 != s1);
+        CHECK(!(s1 == s2));
+        CHECK(!(s2 == s1));
+        CHECK(s1 < s2);
+        CHECK(!(s2 < s1));
+        CHECK(s1 <= s2);
+        CHECK(!(s2 <= s1));
+        CHECK(s2 > s1);
+        CHECK(!(s1 > s2));
+        CHECK(s2 >= s1);
+        CHECK(!(s1 >= s2));
+    }
+
+    {
+        int arr1[] = {1, 2};
+        int arr2[] = {1, 2};
+        span<int> s1 = arr1;
+        span<int> s2 = arr2;
+
+        CHECK(s1 == s2);
+        CHECK(!(s1 != s2));
+        CHECK(!(s1 < s2));
+        CHECK(s1 <= s2);
+        CHECK(!(s1 > s2));
+        CHECK(s1 >= s2);
+        CHECK(s2 == s1);
+        CHECK(!(s2 != s1));
+        CHECK(!(s2 < s1));
+        CHECK(s2 <= s1);
+        CHECK(!(s2 > s1));
+        CHECK(s2 >= s1);
+    }
+
+    {
+        int arr[] = {1, 2, 3};
+
+        span<int> s1 = {&arr[0], 2}; // shorter
+        span<int> s2 = arr;          // longer
+
+        CHECK(s1 != s2);
+        CHECK(s2 != s1);
+        CHECK(!(s1 == s2));
+        CHECK(!(s2 == s1));
+        CHECK(s1 < s2);
+        CHECK(!(s2 < s1));
+        CHECK(s1 <= s2);
+        CHECK(!(s2 <= s1));
+        CHECK(s2 > s1);
+        CHECK(!(s1 > s2));
+        CHECK(s2 >= s1);
+        CHECK(!(s1 >= s2));
+    }
+
+    {
+        int arr1[] = {1, 2}; // smaller
+        int arr2[] = {2, 1}; // bigger
+
+        span<int> s1 = arr1;
+        span<int> s2 = arr2;
+
+        CHECK(s1 != s2);
+        CHECK(s2 != s1);
+        CHECK(!(s1 == s2));
+        CHECK(!(s2 == s1));
+        CHECK(s1 < s2);
+        CHECK(!(s2 < s1));
+        CHECK(s1 <= s2);
+        CHECK(!(s2 <= s1));
+        CHECK(s2 > s1);
+        CHECK(!(s1 > s2));
+        CHECK(s2 >= s1);
+        CHECK(!(s1 >= s2));
+    }
+}
+
+TEST_CASE("as_bytes")
+{
+    int a[] = {1, 2, 3, 4};
+
+    {
+        const span<const int> s = a;
+        CHECK(s.length() == 4);
+        const span<const byte> bs = as_bytes(s);
+        CHECK(static_cast<const void*>(bs.data()) == static_cast<const void*>(s.data()));
+        CHECK(bs.length() == s.length_bytes());
+    }
+
+    {
+        span<int> s;
+        const auto bs = as_bytes(s);
+        CHECK(bs.length() == s.length());
+        CHECK(bs.length() == 0);
+        CHECK(bs.size_bytes() == 0);
+        CHECK(static_cast<const void*>(bs.data()) == static_cast<const void*>(s.data()));
+        CHECK(bs.data() == nullptr);
+    }
+
+    {
+        span<int> s = a;
+        const auto bs = as_bytes(s);
+        CHECK(static_cast<const void*>(bs.data()) == static_cast<const void*>(s.data()));
+        CHECK(bs.length() == s.length_bytes());
+    }
+}
+
+TEST_CASE("as_writeable_bytes")
+{
+    int a[] = {1, 2, 3, 4};
+
+    {
+#ifdef CONFIRM_COMPILATION_ERRORS
+        // you should not be able to get writeable bytes for const objects
+        span<const int> s = a;
+        CHECK(s.length() == 4);
+        span<const byte> bs = as_writeable_bytes(s);
+        CHECK(static_cast<void*>(bs.data()) == static_cast<void*>(s.data()));
+        CHECK(bs.length() == s.length_bytes());
+#endif
+    }
+
+    {
+        span<int> s;
+        const auto bs = as_writeable_bytes(s);
+        CHECK(bs.length() == s.length());
+        CHECK(bs.length() == 0);
+        CHECK(bs.size_bytes() == 0);
+        CHECK(static_cast<void*>(bs.data()) == static_cast<void*>(s.data()));
+        CHECK(bs.data() == nullptr);
+    }
+
+    {
+        span<int> s = a;
+        const auto bs = as_writeable_bytes(s);
+        CHECK(static_cast<void*>(bs.data()) == static_cast<void*>(s.data()));
+        CHECK(bs.length() == s.length_bytes());
+    }
+}
+
+TEST_CASE("fixed_size_conversions")
+{
+    int arr[] = {1, 2, 3, 4};
+
+    // converting to an span from an equal size array is ok
+    span<int, 4> s4 = arr;
+    CHECK(s4.length() == 4);
+
+    // converting to dynamic_range is always ok
+    {
+        span<int> s = s4;
+        CHECK(s.length() == s4.length());
+        static_cast<void>(s);
+    }
+
+// initialization or assignment to static span that REDUCES size is NOT ok
+#ifdef CONFIRM_COMPILATION_ERRORS
+    {
+        span<int, 2> s = arr;
+    }
+    {
+        span<int, 2> s2 = s4;
+        static_cast<void>(s2);
+    }
+#endif
+
+    // even when done dynamically
+    {
+        span<int> s = arr;
+        auto f = [&]() {
+            span<int, 2> s2 = s;
+            static_cast<void>(s2);
+        };
+        CHECK_THROWS_AS(f(), fail_fast);
+    }
+
+    // but doing so explicitly is ok
+
+    // you can convert statically
+    {
+        const span<int, 2> s2 = {arr, 2};
+        static_cast<void>(s2);
+    }
+    {
+        const span<int, 1> s1 = s4.first<1>();
+        static_cast<void>(s1);
+    }
+
+    // ...or dynamically
+    {
+        // NB: implicit conversion to span<int,1> from span<int>
+        span<int, 1> s1 = s4.first(1);
+        static_cast<void>(s1);
+    }
+
+    // initialization or assignment to static span that requires size INCREASE is not ok.
+    int arr2[2] = {1, 2};
+
+#ifdef CONFIRM_COMPILATION_ERRORS
+    {
+        span<int, 4> s3 = arr2;
+    }
+    {
+        span<int, 2> s2 = arr2;
+        span<int, 4> s4a = s2;
+    }
+#endif
+    {
+        auto f = [&]() {
+            span<int, 4> _s4 = {arr2, 2};
+            static_cast<void>(_s4);
+        };
+        CHECK_THROWS_AS(f(), fail_fast);
+    }
+
+    // this should fail - we are trying to assign a small dynamic span to a fixed_size larger one
+    span<int> av = arr2;
+    auto f = [&]() {
+        span<int, 4> _s4 = av;
+        static_cast<void>(_s4);
+    };
+    CHECK_THROWS_AS(f(), fail_fast);
+}
+
+TEST_CASE("interop_with_std_regex")
+{
+    char lat[] = {'1', '2', '3', '4', '5', '6', 'E', 'F', 'G'};
+    span<char> s = lat;
+    const auto f_it = s.begin() + 7;
+
+    std::match_results<span<char>::iterator> match;
+
+    std::regex_match(s.begin(), s.end(), match, std::regex(".*"));
+    CHECK(match.ready());
+    CHECK(!match.empty());
+    CHECK(match[0].matched);
+    CHECK(match[0].first == s.begin());
+    CHECK(match[0].second == s.end());
+
+    std::regex_search(s.begin(), s.end(), match, std::regex("F"));
+    CHECK(match.ready());
+    CHECK(!match.empty());
+    CHECK(match[0].matched);
+    CHECK(match[0].first == f_it);
+    CHECK(match[0].second == (f_it + 1));
+}
+
+TEST_CASE("interop_with_gsl_at")
+{
+    int arr[5] = {1, 2, 3, 4, 5};
+    span<int> s{arr};
+    CHECK((at(s, 0) == 1 && at(s, 1) == 2));
+}
+
+TEST_CASE("default_constructible")
+{
+    CHECK((std::is_default_constructible<span<int>>::value));
+    CHECK((std::is_default_constructible<span<int, 0>>::value));
+    CHECK((!std::is_default_constructible<span<int, 42>>::value));
+}
diff --git a/tests/strided_span_tests.cpp b/tests/strided_span_tests.cpp
index 86666d1..a1ecfcb 100644
--- a/tests/strided_span_tests.cpp
+++ b/tests/strided_span_tests.cpp
@@ -1,748 +1,752 @@
-/////////////////////////////////////////////////////////////////////////////// 
-// 
-// Copyright (c) 2015 Microsoft Corporation. All rights reserved. 
-// 
-// This code is licensed under the MIT License (MIT). 
-// 
-// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 
-// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 
-// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 
-// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 
-// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 
-// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 
-// THE SOFTWARE. 
-// 
+///////////////////////////////////////////////////////////////////////////////
+//
+// Copyright (c) 2015 Microsoft Corporation. All rights reserved.
+//
+// This code is licensed under the MIT License (MIT).
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+// THE SOFTWARE.
+//
 ///////////////////////////////////////////////////////////////////////////////
 
-#include <UnitTest++/UnitTest++.h> 
+#include <catch/catch.hpp>
+
 #include <gsl/multi_span>
 
+#include <iostream>
+#include <list>
+#include <map>
+#include <memory>
 #include <string>
 #include <vector>
-#include <list>
-#include <iostream>
-#include <memory>
-#include <map>
 
 using namespace std;
 using namespace gsl;
 
-namespace 
+namespace
 {
-	struct BaseClass {};
-	struct DerivedClass : BaseClass {};
+struct BaseClass
+{
+};
+struct DerivedClass : BaseClass
+{
+};
 }
 
-SUITE(strided_span_tests)
+TEST_CASE("span_section_test")
 {
-	TEST (span_section_test)
-	{
-		int a[30][4][5];
-		
-		auto av = as_multi_span(a);
-		auto sub = av.section({15, 0, 0}, gsl::index<3>{2, 2, 2});
-		auto subsub = sub.section({1, 0, 0}, gsl::index<3>{1, 1, 1});
-		(void)subsub;
-	}
+    int a[30][4][5];
 
-	TEST(span_section)
-	{
-		std::vector<int> data(5 * 10);
-		std::iota(begin(data), end(data), 0);
-        const multi_span<int, 5, 10> av = as_multi_span(multi_span<int>{data}, dim<5>(), dim<10>());
+    const auto av = as_multi_span(a);
+    const auto sub = av.section({15, 0, 0}, gsl::index<3>{2, 2, 2});
+    const auto subsub = sub.section({1, 0, 0}, gsl::index<3>{1, 1, 1});
+    (void) subsub;
+}
 
-		strided_span<int, 2> av_section_1 = av.section({ 1, 2 }, { 3, 4 });
-		CHECK((av_section_1[{0, 0}] == 12));
-		CHECK((av_section_1[{0, 1}] == 13));
-		CHECK((av_section_1[{1, 0}] == 22));
-		CHECK((av_section_1[{2, 3}] == 35));
+TEST_CASE("span_section")
+{
+    std::vector<int> data(5 * 10);
+    std::iota(begin(data), end(data), 0);
+    const multi_span<int, 5, 10> av = as_multi_span(multi_span<int>{data}, dim<5>(), dim<10>());
 
-		strided_span<int, 2> av_section_2 = av_section_1.section({ 1, 2 }, { 2,2 });
-		CHECK((av_section_2[{0, 0}] == 24));
-		CHECK((av_section_2[{0, 1}] == 25));
-		CHECK((av_section_2[{1, 0}] == 34));
-	}
+    const strided_span<int, 2> av_section_1 = av.section({1, 2}, {3, 4});
+    CHECK((av_section_1[{0, 0}] == 12));
+    CHECK((av_section_1[{0, 1}] == 13));
+    CHECK((av_section_1[{1, 0}] == 22));
+    CHECK((av_section_1[{2, 3}] == 35));
 
-	TEST(strided_span_constructors)
-	{
-		// Check stride constructor
-		{
-			int arr[] = { 1, 2, 3, 4, 5, 6, 7, 8, 9 };
-			const int carr[] = { 1, 2, 3, 4, 5, 6, 7, 8, 9 };
+    const strided_span<int, 2> av_section_2 = av_section_1.section({1, 2}, {2, 2});
+    CHECK((av_section_2[{0, 0}] == 24));
+    CHECK((av_section_2[{0, 1}] == 25));
+    CHECK((av_section_2[{1, 0}] == 34));
+}
 
-			strided_span<int, 1> sav1{ arr, {{9}, {1}} }; // T -> T
-			CHECK(sav1.bounds().index_bounds() == index<1>{ 9 });
-			CHECK(sav1.bounds().stride() == 1);
-			CHECK(sav1[0] == 1 && sav1[8] == 9);
-
-
-			strided_span<const int, 1> sav2{ carr, {{ 4 }, { 2 }} }; // const T -> const T
-			CHECK(sav2.bounds().index_bounds() == index<1>{ 4 });
-			CHECK(sav2.bounds().strides() == index<1>{2});
-			CHECK(sav2[0] == 1 && sav2[3] == 7);
-
-			strided_span<int, 2> sav3{ arr, {{ 2, 2 },{ 6, 2 }} }; // T -> const T
-			CHECK((sav3.bounds().index_bounds() == index<2>{ 2, 2 }));
-			CHECK((sav3.bounds().strides() == index<2>{ 6, 2 }));
-			CHECK((sav3[{0, 0}] == 1 && sav3[{0, 1}] == 3 && sav3[{1, 0}] == 7));
-		}
-
-		// Check multi_span constructor
-		{
-			int arr[] = { 1, 2 };
-
-			// From non-cv-qualified source
-			{
-				const multi_span<int> src = arr;
-
-				strided_span<int, 1> sav{ src, {2, 1} };
-				CHECK(sav.bounds().index_bounds() == index<1>{ 2 });
-				CHECK(sav.bounds().strides() == index<1>{ 1 });
-				CHECK(sav[1] == 2);
-
-#if _MSC_VER > 1800
-				//strided_span<const int, 1> sav_c{ {src}, {2, 1} };
-				strided_span<const int, 1> sav_c{ multi_span<const int>{src}, strided_bounds<1>{2, 1} };
-#else
-				strided_span<const int, 1> sav_c{ multi_span<const int>{src}, strided_bounds<1>{2, 1} };
-#endif
-				CHECK(sav_c.bounds().index_bounds() == index<1>{ 2 });
-				CHECK(sav_c.bounds().strides() == index<1>{ 1 });
-				CHECK(sav_c[1] == 2);
-
-#if _MSC_VER > 1800
-				strided_span<volatile int, 1> sav_v{ src, {2, 1} };
-#else
-				strided_span<volatile int, 1> sav_v{ multi_span<volatile int>{src}, strided_bounds<1>{2, 1} };
-#endif
-				CHECK(sav_v.bounds().index_bounds() == index<1>{ 2 });
-				CHECK(sav_v.bounds().strides() == index<1>{ 1 });
-				CHECK(sav_v[1] == 2);
-
-#if _MSC_VER > 1800
-				strided_span<const volatile int, 1> sav_cv{ src, {2, 1} };
-#else
-				strided_span<const volatile int, 1> sav_cv{ multi_span<const volatile int>{src}, strided_bounds<1>{2, 1} };
-#endif
-				CHECK(sav_cv.bounds().index_bounds() == index<1>{ 2 });
-				CHECK(sav_cv.bounds().strides() == index<1>{ 1 });
-				CHECK(sav_cv[1] == 2);
-			}
-
-			// From const-qualified source
-			{
-				const multi_span<const int> src{ arr };
-
-				strided_span<const int, 1> sav_c{ src, {2, 1} };
-				CHECK(sav_c.bounds().index_bounds() == index<1>{ 2 });
-				CHECK(sav_c.bounds().strides() == index<1>{ 1 });
-				CHECK(sav_c[1] == 2);
-
-#if _MSC_VER > 1800
-				strided_span<const volatile int, 1> sav_cv{ src, {2, 1} };
-#else
-				strided_span<const volatile int, 1> sav_cv{ multi_span<const volatile int>{src}, strided_bounds<1>{2, 1} };
-#endif
-				
-				CHECK(sav_cv.bounds().index_bounds() == index<1>{ 2 });
-				CHECK(sav_cv.bounds().strides() == index<1>{ 1 });
-				CHECK(sav_cv[1] == 2);
-			}
-
-			// From volatile-qualified source
-			{
-				const multi_span<volatile int> src{ arr };
-
-				strided_span<volatile int, 1> sav_v{ src, {2, 1} };
-				CHECK(sav_v.bounds().index_bounds() == index<1>{ 2 });
-				CHECK(sav_v.bounds().strides() == index<1>{ 1 });
-				CHECK(sav_v[1] == 2);
-
-#if _MSC_VER > 1800
-				strided_span<const volatile int, 1> sav_cv{ src, {2, 1} };
-#else
-				strided_span<const volatile int, 1> sav_cv{ multi_span<const volatile int>{src}, strided_bounds<1>{2, 1} };
-#endif
-				CHECK(sav_cv.bounds().index_bounds() == index<1>{ 2 });
-				CHECK(sav_cv.bounds().strides() == index<1>{ 1 });
-				CHECK(sav_cv[1] == 2);
-			}
-
-			// From cv-qualified source
-			{
-				const multi_span<const volatile int> src{ arr };
-
-				strided_span<const volatile int, 1> sav_cv{ src, {2, 1} };
-				CHECK(sav_cv.bounds().index_bounds() == index<1>{ 2 });
-				CHECK(sav_cv.bounds().strides() == index<1>{ 1 });
-				CHECK(sav_cv[1] == 2);
-			}
-		}
-
-		// Check const-casting constructor
-		{
-			int arr[2] = { 4, 5 };
-
-			const multi_span<int, 2> av(arr, 2);
-			multi_span<const int, 2> av2{ av };
-			CHECK(av2[1] == 5);
-
-			static_assert(std::is_convertible<const multi_span<int, 2>, multi_span<const int, 2>>::value, "ctor is not implicit!");
-		
-			const strided_span<int, 1> src{ arr, {2, 1} };
-			strided_span<const int, 1> sav{ src };
-			CHECK(sav.bounds().index_bounds() == index<1>{ 2 });
-			CHECK(sav.bounds().stride() == 1);
-			CHECK(sav[1] == 5);
-			
-			static_assert(std::is_convertible<const strided_span<int, 1>, strided_span<const int, 1>>::value, "ctor is not implicit!");
-		}
-
-		// Check copy constructor
-		{
-			int arr1[2] = { 3, 4 };
-			const strided_span<int, 1> src1{ arr1, {2, 1} };
-			strided_span<int, 1> sav1{ src1 };
- 
-			CHECK(sav1.bounds().index_bounds() == index<1>{ 2 });
-			CHECK(sav1.bounds().stride() == 1);
-			CHECK(sav1[0] == 3);
-
-			int arr2[6] = { 1, 2, 3, 4, 5, 6 };
-			const strided_span<const int, 2> src2{ arr2, {{ 3, 2 }, { 2, 1 }} };
-			strided_span<const int, 2> sav2{ src2 };
-			CHECK((sav2.bounds().index_bounds() == index<2>{ 3, 2 }));
-			CHECK((sav2.bounds().strides() == index<2>{ 2, 1 }));
-			CHECK((sav2[{0, 0}] == 1 && sav2[{2, 0}] == 5));
-		}
-
-		// Check const-casting assignment operator
-		{
-			int arr1[2] = { 1, 2 };
-			int arr2[6] = { 3, 4, 5, 6, 7, 8 };
-
-			const strided_span<int, 1> src{ arr1, {{2}, {1}} };
-			strided_span<const int, 1> sav{ arr2, {{3}, {2}} };
-			strided_span<const int, 1>& sav_ref = (sav = src);
-			CHECK(sav.bounds().index_bounds() == index<1>{ 2 });
-			CHECK(sav.bounds().strides() == index<1>{ 1 });
-			CHECK(sav[0] == 1);
-			CHECK(&sav_ref == &sav);
-		}
-		
-		// Check copy assignment operator
-		{
-			int arr1[2] = { 3, 4 };
-			int arr1b[1] = { 0 };
-			const strided_span<int, 1> src1{ arr1, {2, 1} };
-			strided_span<int, 1> sav1{ arr1b, {1, 1} };
-			strided_span<int, 1>& sav1_ref = (sav1 = src1);
-			CHECK(sav1.bounds().index_bounds() == index<1>{ 2 });
-			CHECK(sav1.bounds().strides() == index<1>{ 1 });
-			CHECK(sav1[0] == 3);
-			CHECK(&sav1_ref == &sav1);
-
-			const int arr2[6] = { 1, 2, 3, 4, 5, 6 };
-			const int arr2b[1] = { 0 };
-			const strided_span<const int, 2> src2{ arr2, {{ 3, 2 },{ 2, 1 }} };
-			strided_span<const int, 2> sav2{ arr2b, {{ 1, 1 },{ 1, 1 }} };
-			strided_span<const int, 2>& sav2_ref = (sav2 = src2);
-			CHECK((sav2.bounds().index_bounds() == index<2>{ 3, 2 }));
-			CHECK((sav2.bounds().strides() == index<2>{ 2, 1 }));
-			CHECK((sav2[{0, 0}] == 1 && sav2[{2, 0}] == 5));
-			CHECK(&sav2_ref == &sav2);
-		}
-	}
-
-	TEST(strided_span_slice)
-	{
-		std::vector<int> data(5 * 10);
-		std::iota(begin(data), end(data), 0);
-        const multi_span<int, 5, 10> src = as_multi_span(multi_span<int>{data}, dim<5>(), dim<10>());
-
-		const strided_span<int, 2> sav{ src, {{5, 10}, {10, 1}} };
-#ifdef CONFIRM_COMPILATION_ERRORS
-		const strided_span<const int, 2> csav{ {src},{ { 5, 10 },{ 10, 1 } } };
-#endif
-		const strided_span<const int, 2> csav{ multi_span<const int, 5, 10>{ src }, { { 5, 10 },{ 10, 1 } } };
-
-		strided_span<int, 1> sav_sl = sav[2];
-		CHECK(sav_sl[0] == 20);
-		CHECK(sav_sl[9] == 29);
-
-		strided_span<const int, 1> csav_sl = sav[3];
-		CHECK(csav_sl[0] == 30);
-		CHECK(csav_sl[9] == 39);
-
-		CHECK(sav[4][0] == 40);
-		CHECK(sav[4][9] == 49);
-	}
-
-	TEST(strided_span_column_major)
-	{
-		// strided_span may be used to accomodate more peculiar
-		// use cases, such as column-major multidimensional array
-		// (aka. "FORTRAN" layout).
-
-		int cm_array[3 * 5] = {
-			1, 4, 7, 10, 13,
-			2, 5, 8, 11, 14,
-			3, 6, 9, 12, 15
-		};
-		strided_span<int, 2> cm_sav{ cm_array, {{ 5, 3 },{ 1, 5 }} };
-
-		// Accessing elements
-		CHECK((cm_sav[{0, 0}] == 1));
-		CHECK((cm_sav[{0, 1}] == 2));
-		CHECK((cm_sav[{1, 0}] == 4));
-		CHECK((cm_sav[{4, 2}] == 15));
-
-		// Slice
-		strided_span<int, 1> cm_sl = cm_sav[3];
-
-		CHECK(cm_sl[0] == 10);
-		CHECK(cm_sl[1] == 11);
-		CHECK(cm_sl[2] == 12);
-
-		// Section 
-		strided_span<int, 2> cm_sec = cm_sav.section( { 2, 1 }, { 3, 2 });
-
-		CHECK((cm_sec.bounds().index_bounds() == index<2>{3, 2}));
-		CHECK((cm_sec[{0, 0}] == 8));
-		CHECK((cm_sec[{0, 1}] == 9));
-		CHECK((cm_sec[{1, 0}] == 11));
-		CHECK((cm_sec[{2, 1}] == 15));
-	}
-
-	TEST(strided_span_bounds)
-	{
-		int arr[] = { 0, 1, 2, 3 };
-		multi_span<int> av(arr);
-
-		{
-			// incorrect sections
-			
-			CHECK_THROW(av.section(0, 0)[0], fail_fast);
-			CHECK_THROW(av.section(1, 0)[0], fail_fast);
-			CHECK_THROW(av.section(1, 1)[1], fail_fast);
-			
-			CHECK_THROW(av.section(2, 5), fail_fast);
-			CHECK_THROW(av.section(5, 2), fail_fast);
-			CHECK_THROW(av.section(5, 0), fail_fast);
-			CHECK_THROW(av.section(0, 5), fail_fast);
-			CHECK_THROW(av.section(5, 5), fail_fast);
-		}
-
-		{
-			// zero stride
-			strided_span<int, 1> sav{ av,{ { 4 },{} } };
-			CHECK(sav[0] == 0);
-			CHECK(sav[3] == 0);
-			CHECK_THROW(sav[4], fail_fast);
-		}
-
-		{
-			// zero extent
-			strided_span<int, 1> sav{ av,{ {},{ 1 } } };
-			CHECK_THROW(sav[0], fail_fast);
-		}
-
-		{
-			// zero extent and stride
-			strided_span<int, 1> sav{ av,{ {},{} } };
-			CHECK_THROW(sav[0], fail_fast);
-		}
-
-		{
-			// strided array ctor with matching strided bounds 
-			strided_span<int, 1> sav{ arr,{ 4, 1 } };
-			CHECK(sav.bounds().index_bounds() == index<1>{ 4 });
-			CHECK(sav[3] == 3);
-			CHECK_THROW(sav[4], fail_fast);
-		}
-
-		{
-			// strided array ctor with smaller strided bounds 
-			strided_span<int, 1> sav{ arr,{ 2, 1 } };
-			CHECK(sav.bounds().index_bounds() == index<1>{ 2 });
-			CHECK(sav[1] == 1);
-			CHECK_THROW(sav[2], fail_fast);
-		}
-
-		{
-			// strided array ctor with fitting irregular bounds 
-			strided_span<int, 1> sav{ arr,{ 2, 3 } };
-			CHECK(sav.bounds().index_bounds() == index<1>{ 2 });
-			CHECK(sav[0] == 0);
-			CHECK(sav[1] == 3);
-			CHECK_THROW(sav[2], fail_fast);
-		}
-
-		{
-			// bounds cross data boundaries - from static arrays
-			CHECK_THROW((strided_span<int, 1> { arr, { 3, 2 } }), fail_fast);
-			CHECK_THROW((strided_span<int, 1> { arr, { 3, 3 } }), fail_fast);
-			CHECK_THROW((strided_span<int, 1> { arr, { 4, 5 } }), fail_fast);
-			CHECK_THROW((strided_span<int, 1> { arr, { 5, 1 } }), fail_fast);
-			CHECK_THROW((strided_span<int, 1> { arr, { 5, 5 } }), fail_fast);
-		}
-
-		{
-			// bounds cross data boundaries - from array view
-			CHECK_THROW((strided_span<int, 1> { av, { 3, 2 } }), fail_fast);
-			CHECK_THROW((strided_span<int, 1> { av, { 3, 3 } }), fail_fast);
-			CHECK_THROW((strided_span<int, 1> { av, { 4, 5 } }), fail_fast);
-			CHECK_THROW((strided_span<int, 1> { av, { 5, 1 } }), fail_fast);
-			CHECK_THROW((strided_span<int, 1> { av, { 5, 5 } }), fail_fast);
-		}
-
-		{
-			// bounds cross data boundaries - from dynamic arrays
-			CHECK_THROW((strided_span<int, 1> { av.data(), 4, { 3, 2 } }), fail_fast);
-			CHECK_THROW((strided_span<int, 1> { av.data(), 4, { 3, 3 } }), fail_fast);
-			CHECK_THROW((strided_span<int, 1> { av.data(), 4, { 4, 5 } }), fail_fast);
-			CHECK_THROW((strided_span<int, 1> { av.data(), 4, { 5, 1 } }), fail_fast);
-			CHECK_THROW((strided_span<int, 1> { av.data(), 4, { 5, 5 } }), fail_fast);
-			CHECK_THROW((strided_span<int, 1> { av.data(), 2, { 2, 2 } }), fail_fast);
-		}
-
-#ifdef CONFIRM_COMPILATION_ERRORS
-		{
-			strided_span<int, 1> sav0{ av.data(), { 3, 2 } };
-			strided_span<int, 1> sav1{ arr, { 1 } };
-			strided_span<int, 1> sav2{ arr, { 1,1,1 } };
-			strided_span<int, 1> sav3{ av, { 1 } };
-			strided_span<int, 1> sav4{ av, { 1,1,1 } };
-			strided_span<int, 2> sav5{ av.as_multi_span(dim<2>(), dim<2>()), { 1 } };
-			strided_span<int, 2> sav6{ av.as_multi_span(dim<2>(), dim<2>()), { 1,1,1 } };
-			strided_span<int, 2> sav7{ av.as_multi_span(dim<2>(), dim<2>()), { { 1,1 },{ 1,1 },{ 1,1 } } };
-
-			index<1> index{ 0, 1 };
-			strided_span<int, 1> sav8{ arr,{ 1,{ 1,1 } } };
-			strided_span<int, 1> sav9{ arr,{ { 1,1 },{ 1,1 } } };
-			strided_span<int, 1> sav10{ av,{ 1,{ 1,1 } } };
-			strided_span<int, 1> sav11{ av,{ { 1,1 },{ 1,1 } } };
-			strided_span<int, 2> sav12{ av.as_multi_span(dim<2>(), dim<2>()),{ { 1 },{ 1 } } };
-			strided_span<int, 2> sav13{ av.as_multi_span(dim<2>(), dim<2>()),{ { 1 },{ 1,1,1 } } };
-			strided_span<int, 2> sav14{ av.as_multi_span(dim<2>(), dim<2>()),{ { 1,1,1 },{ 1 } } };
-		}
-#endif
-	}
-
-	TEST(strided_span_type_conversion)
-	{
-		int arr[] = { 0, 1, 2, 3 };
-		multi_span<int> av(arr);
-
-		{
-			strided_span<int, 1> sav{ av.data(), av.size(), { av.size() / 2, 2 } };
-#ifdef CONFIRM_COMPILATION_ERRORS
-			strided_span<long, 1> lsav1 = sav.as_strided_span<long, 1>();
-#endif
-		}
-		{
-			strided_span<int, 1> sav{ av, { av.size() / 2, 2 } };
-#ifdef CONFIRM_COMPILATION_ERRORS
-			strided_span<long, 1> lsav1 = sav.as_strided_span<long, 1>();
-#endif
-		}
-
-		multi_span<const byte, dynamic_range> bytes = as_bytes(av);
-
-		// retype strided array with regular strides - from raw data
-		{
-			strided_bounds<2> bounds{ { 2, bytes.size() / 4 }, { bytes.size() / 2, 1 } };
-			strided_span<const byte, 2> sav2{ bytes.data(), bytes.size(), bounds };
-			strided_span<const int, 2> sav3 = sav2.as_strided_span<const int>();
-			CHECK(sav3[0][0] == 0);
-			CHECK(sav3[1][0] == 2);
-			CHECK_THROW(sav3[1][1], fail_fast);
-			CHECK_THROW(sav3[0][1], fail_fast);
-		}
-
-		// retype strided array with regular strides - from multi_span
-		{
-			strided_bounds<2> bounds{ { 2, bytes.size() / 4 }, { bytes.size() / 2, 1 } };
-			multi_span<const byte, 2, dynamic_range> bytes2 = as_multi_span(bytes, dim<2>(), dim(bytes.size() / 2));
-			strided_span<const byte, 2> sav2{ bytes2, bounds };
-			strided_span<int, 2> sav3 = sav2.as_strided_span<int>();
-			CHECK(sav3[0][0] == 0);
-			CHECK(sav3[1][0] == 2);
-			CHECK_THROW(sav3[1][1], fail_fast);
-			CHECK_THROW(sav3[0][1], fail_fast);
-		}
-
-		// retype strided array with not enough elements - last dimension of the array is too small
-		{
-			strided_bounds<2> bounds{ { 4,2 },{ 4, 1 } };
-			multi_span<const byte, 2, dynamic_range> bytes2 = as_multi_span(bytes, dim<2>(), dim(bytes.size() / 2));
-			strided_span<const byte, 2> sav2{ bytes2, bounds };
-			CHECK_THROW(sav2.as_strided_span<int>(), fail_fast);
-		}
-
-		// retype strided array with not enough elements - strides are too small
-		{
-			strided_bounds<2> bounds{ { 4,2 },{ 2, 1 } };
-			multi_span<const byte, 2, dynamic_range> bytes2 = as_multi_span(bytes, dim<2>(), dim(bytes.size() / 2));
-			strided_span<const byte, 2> sav2{ bytes2, bounds };
-			CHECK_THROW(sav2.as_strided_span<int>(), fail_fast);
-		}
-
-		// retype strided array with not enough elements - last dimension does not divide by the new typesize
-		{
-			strided_bounds<2> bounds{ { 2,6 },{ 4, 1 } };
-			multi_span<const byte, 2, dynamic_range> bytes2 = as_multi_span(bytes, dim<2>(), dim(bytes.size() / 2));
-			strided_span<const byte, 2> sav2{ bytes2, bounds };
-			CHECK_THROW(sav2.as_strided_span<int>(), fail_fast);
-		}
-
-		// retype strided array with not enough elements - strides does not divide by the new typesize
-		{
-			strided_bounds<2> bounds{ { 2, 1 },{ 6, 1 } };
-			multi_span<const byte, 2, dynamic_range> bytes2 = as_multi_span(bytes, dim<2>(), dim(bytes.size() / 2));
-			strided_span<const byte, 2> sav2{ bytes2, bounds };
-			CHECK_THROW(sav2.as_strided_span<int>(), fail_fast);
-		}
-
-		// retype strided array with irregular strides - from raw data
-		{
-			strided_bounds<1> bounds{ bytes.size() / 2, 2 };
-			strided_span<const byte, 1> sav2{ bytes.data(), bytes.size(), bounds };
-			CHECK_THROW(sav2.as_strided_span<int>(), fail_fast);
-		}
-
-		// retype strided array with irregular strides - from multi_span
-		{
-			strided_bounds<1> bounds{ bytes.size() / 2, 2 };
-			strided_span<const byte, 1> sav2{ bytes, bounds };
-			CHECK_THROW(sav2.as_strided_span<int>(), fail_fast);
-		}
-	}
-
-	TEST(empty_strided_spans)
-	{
-		{
-			multi_span<int, 0> empty_av(nullptr);
-			strided_span<int, 1> empty_sav{ empty_av, { 0, 1 } };
-
-			CHECK(empty_sav.bounds().index_bounds() == index<1>{ 0 });
-			CHECK_THROW(empty_sav[0], fail_fast);
-			CHECK_THROW(empty_sav.begin()[0], fail_fast);
-			CHECK_THROW(empty_sav.cbegin()[0], fail_fast);
-
-			for (auto& v : empty_sav)
-			{
-                (void)v;
-				CHECK(false);
-			}
-		}
-
-		{
-			strided_span<int, 1> empty_sav{ nullptr, 0, { 0, 1 } };
-
-			CHECK(empty_sav.bounds().index_bounds() == index<1>{ 0 });
-			CHECK_THROW(empty_sav[0], fail_fast);
-			CHECK_THROW(empty_sav.begin()[0], fail_fast);
-			CHECK_THROW(empty_sav.cbegin()[0], fail_fast);
-
-			for (auto& v : empty_sav)
-			{
-                (void)v;
-				CHECK(false);
-			}
-		}
-	}
-
-    void iterate_every_other_element(multi_span<int, dynamic_range> av)
+TEST_CASE("strided_span_constructors")
+{
+    // Check stride constructor
     {
-        // pick every other element
+        int arr[] = {1, 2, 3, 4, 5, 6, 7, 8, 9};
+        const int carr[] = {1, 2, 3, 4, 5, 6, 7, 8, 9};
 
-        auto length = av.size() / 2;
-#if _MSC_VER > 1800
-        auto bounds = strided_bounds<1>({length}, {2});
-#else
-        auto bounds = strided_bounds<1>(index<1>{ length }, index<1>{ 2 });
-#endif
-        strided_span<int, 1> strided(&av.data()[1], av.size() - 1, bounds);
+        strided_span<int, 1> sav1{arr, {{9}, {1}}}; // T -> T
+        CHECK(sav1.bounds().index_bounds() == index<1>{9});
+        CHECK(sav1.bounds().stride() == 1);
+        CHECK((sav1[0] == 1 && sav1[8] == 9));
 
-        CHECK(strided.size() == length);
-        CHECK(strided.bounds().index_bounds()[0] == length);
-        for (auto i = 0; i < strided.size(); ++i)
+        strided_span<const int, 1> sav2{carr, {{4}, {2}}}; // const T -> const T
+        CHECK(sav2.bounds().index_bounds() == index<1>{4});
+        CHECK(sav2.bounds().strides() == index<1>{2});
+        CHECK((sav2[0] == 1 && sav2[3] == 7));
+
+        strided_span<int, 2> sav3{arr, {{2, 2}, {6, 2}}}; // T -> const T
+        CHECK((sav3.bounds().index_bounds() == index<2>{2, 2}));
+        CHECK((sav3.bounds().strides() == index<2>{6, 2}));
+        CHECK((sav3[{0, 0}] == 1 && sav3[{0, 1}] == 3 && sav3[{1, 0}] == 7));
+    }
+
+    // Check multi_span constructor
+    {
+        int arr[] = {1, 2};
+
+        // From non-cv-qualified source
         {
-            CHECK(strided[i] == av[2 * i + 1]);
+            const multi_span<int> src = arr;
+
+            strided_span<int, 1> sav{src, {2, 1}};
+            CHECK(sav.bounds().index_bounds() == index<1>{2});
+            CHECK(sav.bounds().strides() == index<1>{1});
+            CHECK(sav[1] == 2);
+
+#if _MSC_VER > 1800
+            // strided_span<const int, 1> sav_c{ {src}, {2, 1} };
+            strided_span<const int, 1> sav_c{multi_span<const int>{src},
+                                             strided_bounds<1>{2, 1}};
+#else
+            strided_span<const int, 1> sav_c{multi_span<const int>{src},
+                                             strided_bounds<1>{2, 1}};
+#endif
+            CHECK(sav_c.bounds().index_bounds() == index<1>{2});
+            CHECK(sav_c.bounds().strides() == index<1>{1});
+            CHECK(sav_c[1] == 2);
+
+#if _MSC_VER > 1800
+            strided_span<volatile int, 1> sav_v{src, {2, 1}};
+#else
+            strided_span<volatile int, 1> sav_v{multi_span<volatile int>{src},
+                                                strided_bounds<1>{2, 1}};
+#endif
+            CHECK(sav_v.bounds().index_bounds() == index<1>{2});
+            CHECK(sav_v.bounds().strides() == index<1>{1});
+            CHECK(sav_v[1] == 2);
+
+#if _MSC_VER > 1800
+            strided_span<const volatile int, 1> sav_cv{src, {2, 1}};
+#else
+            strided_span<const volatile int, 1> sav_cv{multi_span<const volatile int>{src},
+                                                       strided_bounds<1>{2, 1}};
+#endif
+            CHECK(sav_cv.bounds().index_bounds() == index<1>{2});
+            CHECK(sav_cv.bounds().strides() == index<1>{1});
+            CHECK(sav_cv[1] == 2);
         }
 
-        int idx = 0;
-        for (auto num : strided)
+        // From const-qualified source
         {
-            CHECK(num == av[2 * idx + 1]);
-            idx++;
+            const multi_span<const int> src{arr};
+
+            strided_span<const int, 1> sav_c{src, {2, 1}};
+            CHECK(sav_c.bounds().index_bounds() == index<1>{2});
+            CHECK(sav_c.bounds().strides() == index<1>{1});
+            CHECK(sav_c[1] == 2);
+
+#if _MSC_VER > 1800
+            strided_span<const volatile int, 1> sav_cv{src, {2, 1}};
+#else
+            strided_span<const volatile int, 1> sav_cv{multi_span<const volatile int>{src},
+                                                       strided_bounds<1>{2, 1}};
+#endif
+
+            CHECK(sav_cv.bounds().index_bounds() == index<1>{2});
+            CHECK(sav_cv.bounds().strides() == index<1>{1});
+            CHECK(sav_cv[1] == 2);
+        }
+
+        // From volatile-qualified source
+        {
+            const multi_span<volatile int> src{arr};
+
+            strided_span<volatile int, 1> sav_v{src, {2, 1}};
+            CHECK(sav_v.bounds().index_bounds() == index<1>{2});
+            CHECK(sav_v.bounds().strides() == index<1>{1});
+            CHECK(sav_v[1] == 2);
+
+#if _MSC_VER > 1800
+            strided_span<const volatile int, 1> sav_cv{src, {2, 1}};
+#else
+            strided_span<const volatile int, 1> sav_cv{multi_span<const volatile int>{src},
+                                                       strided_bounds<1>{2, 1}};
+#endif
+            CHECK(sav_cv.bounds().index_bounds() == index<1>{2});
+            CHECK(sav_cv.bounds().strides() == index<1>{1});
+            CHECK(sav_cv[1] == 2);
+        }
+
+        // From cv-qualified source
+        {
+            const multi_span<const volatile int> src{arr};
+
+            strided_span<const volatile int, 1> sav_cv{src, {2, 1}};
+            CHECK(sav_cv.bounds().index_bounds() == index<1>{2});
+            CHECK(sav_cv.bounds().strides() == index<1>{1});
+            CHECK(sav_cv[1] == 2);
         }
     }
 
-    TEST(strided_span_section_iteration)
+    // Check const-casting constructor
     {
-        int arr[8] = {4,0,5,1,6,2,7,3};
+        int arr[2] = {4, 5};
 
-        // static bounds
-        {
-            multi_span<int, 8> av(arr, 8);
-            iterate_every_other_element(av);
-        }
+        const multi_span<int, 2> av(arr, 2);
+        multi_span<const int, 2> av2{av};
+        CHECK(av2[1] == 5);
 
-        // dynamic bounds
-        {
-            multi_span<int, dynamic_range> av(arr, 8);
-            iterate_every_other_element(av);
+        static_assert(
+            std::is_convertible<const multi_span<int, 2>, multi_span<const int, 2>>::value,
+            "ctor is not implicit!");
+
+        const strided_span<int, 1> src{arr, {2, 1}};
+        strided_span<const int, 1> sav{src};
+        CHECK(sav.bounds().index_bounds() == index<1>{2});
+        CHECK(sav.bounds().stride() == 1);
+        CHECK(sav[1] == 5);
+
+        static_assert(
+            std::is_convertible<const strided_span<int, 1>, strided_span<const int, 1>>::value,
+            "ctor is not implicit!");
+    }
+
+    // Check copy constructor
+    {
+        int arr1[2] = {3, 4};
+        const strided_span<int, 1> src1{arr1, {2, 1}};
+        strided_span<int, 1> sav1{src1};
+
+        CHECK(sav1.bounds().index_bounds() == index<1>{2});
+        CHECK(sav1.bounds().stride() == 1);
+        CHECK(sav1[0] == 3);
+
+        int arr2[6] = {1, 2, 3, 4, 5, 6};
+        const strided_span<const int, 2> src2{arr2, {{3, 2}, {2, 1}}};
+        strided_span<const int, 2> sav2{src2};
+        CHECK((sav2.bounds().index_bounds() == index<2>{3, 2}));
+        CHECK((sav2.bounds().strides() == index<2>{2, 1}));
+        CHECK((sav2[{0, 0}] == 1 && sav2[{2, 0}] == 5));
+    }
+
+    // Check const-casting assignment operator
+    {
+        int arr1[2] = {1, 2};
+        int arr2[6] = {3, 4, 5, 6, 7, 8};
+
+        const strided_span<int, 1> src{arr1, {{2}, {1}}};
+        strided_span<const int, 1> sav{arr2, {{3}, {2}}};
+        strided_span<const int, 1>& sav_ref = (sav = src);
+        CHECK(sav.bounds().index_bounds() == index<1>{2});
+        CHECK(sav.bounds().strides() == index<1>{1});
+        CHECK(sav[0] == 1);
+        CHECK(&sav_ref == &sav);
+    }
+
+    // Check copy assignment operator
+    {
+        int arr1[2] = {3, 4};
+        int arr1b[1] = {0};
+        const strided_span<int, 1> src1{arr1, {2, 1}};
+        strided_span<int, 1> sav1{arr1b, {1, 1}};
+        strided_span<int, 1>& sav1_ref = (sav1 = src1);
+        CHECK(sav1.bounds().index_bounds() == index<1>{2});
+        CHECK(sav1.bounds().strides() == index<1>{1});
+        CHECK(sav1[0] == 3);
+        CHECK(&sav1_ref == &sav1);
+
+        const int arr2[6] = {1, 2, 3, 4, 5, 6};
+        const int arr2b[1] = {0};
+        const strided_span<const int, 2> src2{arr2, {{3, 2}, {2, 1}}};
+        strided_span<const int, 2> sav2{arr2b, {{1, 1}, {1, 1}}};
+        strided_span<const int, 2>& sav2_ref = (sav2 = src2);
+        CHECK((sav2.bounds().index_bounds() == index<2>{3, 2}));
+        CHECK((sav2.bounds().strides() == index<2>{2, 1}));
+        CHECK((sav2[{0, 0}] == 1 && sav2[{2, 0}] == 5));
+        CHECK(&sav2_ref == &sav2);
+    }
+}
+
+TEST_CASE("strided_span_slice")
+{
+    std::vector<int> data(5 * 10);
+    std::iota(begin(data), end(data), 0);
+    const multi_span<int, 5, 10> src =
+        as_multi_span(multi_span<int>{data}, dim<5>(), dim<10>());
+
+    const strided_span<int, 2> sav{src, {{5, 10}, {10, 1}}};
+#ifdef CONFIRM_COMPILATION_ERRORS
+    const strided_span<const int, 2> csav{{src}, {{5, 10}, {10, 1}}};
+#endif
+    const strided_span<const int, 2> csav{multi_span<const int, 5, 10>{src},
+                                          {{5, 10}, {10, 1}}};
+
+    strided_span<int, 1> sav_sl = sav[2];
+    CHECK(sav_sl[0] == 20);
+    CHECK(sav_sl[9] == 29);
+
+    strided_span<const int, 1> csav_sl = sav[3];
+    CHECK(csav_sl[0] == 30);
+    CHECK(csav_sl[9] == 39);
+
+    CHECK(sav[4][0] == 40);
+    CHECK(sav[4][9] == 49);
+}
+
+TEST_CASE("strided_span_column_major")
+{
+    // strided_span may be used to accomodate more peculiar
+    // use cases, such as column-major multidimensional array
+    // (aka. "FORTRAN" layout).
+
+    int cm_array[3 * 5] = {1, 4, 7, 10, 13, 2, 5, 8, 11, 14, 3, 6, 9, 12, 15};
+    strided_span<int, 2> cm_sav{cm_array, {{5, 3}, {1, 5}}};
+
+    // Accessing elements
+    CHECK((cm_sav[{0, 0}] == 1));
+    CHECK((cm_sav[{0, 1}] == 2));
+    CHECK((cm_sav[{1, 0}] == 4));
+    CHECK((cm_sav[{4, 2}] == 15));
+
+    // Slice
+    strided_span<int, 1> cm_sl = cm_sav[3];
+
+    CHECK(cm_sl[0] == 10);
+    CHECK(cm_sl[1] == 11);
+    CHECK(cm_sl[2] == 12);
+
+    // Section
+    strided_span<int, 2> cm_sec = cm_sav.section({2, 1}, {3, 2});
+
+    CHECK((cm_sec.bounds().index_bounds() == index<2>{3, 2}));
+    CHECK((cm_sec[{0, 0}] == 8));
+    CHECK((cm_sec[{0, 1}] == 9));
+    CHECK((cm_sec[{1, 0}] == 11));
+    CHECK((cm_sec[{2, 1}] == 15));
+}
+
+TEST_CASE("strided_span_bounds")
+{
+    int arr[] = {0, 1, 2, 3};
+    multi_span<int> av(arr);
+
+    {
+        // incorrect sections
+
+        CHECK_THROWS_AS(av.section(0, 0)[0], fail_fast);
+        CHECK_THROWS_AS(av.section(1, 0)[0], fail_fast);
+        CHECK_THROWS_AS(av.section(1, 1)[1], fail_fast);
+
+        CHECK_THROWS_AS(av.section(2, 5), fail_fast);
+        CHECK_THROWS_AS(av.section(5, 2), fail_fast);
+        CHECK_THROWS_AS(av.section(5, 0), fail_fast);
+        CHECK_THROWS_AS(av.section(0, 5), fail_fast);
+        CHECK_THROWS_AS(av.section(5, 5), fail_fast);
+    }
+
+    {
+        // zero stride
+        strided_span<int, 1> sav{av, {{4}, {}}};
+        CHECK(sav[0] == 0);
+        CHECK(sav[3] == 0);
+        CHECK_THROWS_AS(sav[4], fail_fast);
+    }
+
+    {
+        // zero extent
+        strided_span<int, 1> sav{av, {{}, {1}}};
+        CHECK_THROWS_AS(sav[0], fail_fast);
+    }
+
+    {
+        // zero extent and stride
+        strided_span<int, 1> sav{av, {{}, {}}};
+        CHECK_THROWS_AS(sav[0], fail_fast);
+    }
+
+    {
+        // strided array ctor with matching strided bounds
+        strided_span<int, 1> sav{arr, {4, 1}};
+        CHECK(sav.bounds().index_bounds() == index<1>{4});
+        CHECK(sav[3] == 3);
+        CHECK_THROWS_AS(sav[4], fail_fast);
+    }
+
+    {
+        // strided array ctor with smaller strided bounds
+        strided_span<int, 1> sav{arr, {2, 1}};
+        CHECK(sav.bounds().index_bounds() == index<1>{2});
+        CHECK(sav[1] == 1);
+        CHECK_THROWS_AS(sav[2], fail_fast);
+    }
+
+    {
+        // strided array ctor with fitting irregular bounds
+        strided_span<int, 1> sav{arr, {2, 3}};
+        CHECK(sav.bounds().index_bounds() == index<1>{2});
+        CHECK(sav[0] == 0);
+        CHECK(sav[1] == 3);
+        CHECK_THROWS_AS(sav[2], fail_fast);
+    }
+
+    {
+        // bounds cross data boundaries - from static arrays
+        CHECK_THROWS_AS((strided_span<int, 1>{arr, {3, 2}}), fail_fast);
+        CHECK_THROWS_AS((strided_span<int, 1>{arr, {3, 3}}), fail_fast);
+        CHECK_THROWS_AS((strided_span<int, 1>{arr, {4, 5}}), fail_fast);
+        CHECK_THROWS_AS((strided_span<int, 1>{arr, {5, 1}}), fail_fast);
+        CHECK_THROWS_AS((strided_span<int, 1>{arr, {5, 5}}), fail_fast);
+    }
+
+    {
+        // bounds cross data boundaries - from array view
+        CHECK_THROWS_AS((strided_span<int, 1>{av, {3, 2}}), fail_fast);
+        CHECK_THROWS_AS((strided_span<int, 1>{av, {3, 3}}), fail_fast);
+        CHECK_THROWS_AS((strided_span<int, 1>{av, {4, 5}}), fail_fast);
+        CHECK_THROWS_AS((strided_span<int, 1>{av, {5, 1}}), fail_fast);
+        CHECK_THROWS_AS((strided_span<int, 1>{av, {5, 5}}), fail_fast);
+    }
+
+    {
+        // bounds cross data boundaries - from dynamic arrays
+        CHECK_THROWS_AS((strided_span<int, 1>{av.data(), 4, {3, 2}}), fail_fast);
+        CHECK_THROWS_AS((strided_span<int, 1>{av.data(), 4, {3, 3}}), fail_fast);
+        CHECK_THROWS_AS((strided_span<int, 1>{av.data(), 4, {4, 5}}), fail_fast);
+        CHECK_THROWS_AS((strided_span<int, 1>{av.data(), 4, {5, 1}}), fail_fast);
+        CHECK_THROWS_AS((strided_span<int, 1>{av.data(), 4, {5, 5}}), fail_fast);
+        CHECK_THROWS_AS((strided_span<int, 1>{av.data(), 2, {2, 2}}), fail_fast);
+    }
+
+#ifdef CONFIRM_COMPILATION_ERRORS
+    {
+        strided_span<int, 1> sav0{av.data(), {3, 2}};
+        strided_span<int, 1> sav1{arr, {1}};
+        strided_span<int, 1> sav2{arr, {1, 1, 1}};
+        strided_span<int, 1> sav3{av, {1}};
+        strided_span<int, 1> sav4{av, {1, 1, 1}};
+        strided_span<int, 2> sav5{av.as_multi_span(dim<2>(), dim<2>()), {1}};
+        strided_span<int, 2> sav6{av.as_multi_span(dim<2>(), dim<2>()), {1, 1, 1}};
+        strided_span<int, 2> sav7{av.as_multi_span(dim<2>(), dim<2>()),
+                                  {{1, 1}, {1, 1}, {1, 1}}};
+
+        index<1> index{0, 1};
+        strided_span<int, 1> sav8{arr, {1, {1, 1}}};
+        strided_span<int, 1> sav9{arr, {{1, 1}, {1, 1}}};
+        strided_span<int, 1> sav10{av, {1, {1, 1}}};
+        strided_span<int, 1> sav11{av, {{1, 1}, {1, 1}}};
+        strided_span<int, 2> sav12{av.as_multi_span(dim<2>(), dim<2>()), {{1}, {1}}};
+        strided_span<int, 2> sav13{av.as_multi_span(dim<2>(), dim<2>()), {{1}, {1, 1, 1}}};
+        strided_span<int, 2> sav14{av.as_multi_span(dim<2>(), dim<2>()), {{1, 1, 1}, {1}}};
+    }
+#endif
+}
+
+TEST_CASE("strided_span_type_conversion")
+{
+    int arr[] = {0, 1, 2, 3};
+    multi_span<int> av(arr);
+
+    {
+        strided_span<int, 1> sav{av.data(), av.size(), {av.size() / 2, 2}};
+#ifdef CONFIRM_COMPILATION_ERRORS
+        strided_span<long, 1> lsav1 = sav.as_strided_span<long, 1>();
+#endif
+    }
+    {
+        strided_span<int, 1> sav{av, {av.size() / 2, 2}};
+#ifdef CONFIRM_COMPILATION_ERRORS
+        strided_span<long, 1> lsav1 = sav.as_strided_span<long, 1>();
+#endif
+    }
+
+    multi_span<const byte, dynamic_range> bytes = as_bytes(av);
+
+    // retype strided array with regular strides - from raw data
+    {
+        strided_bounds<2> bounds{{2, bytes.size() / 4}, {bytes.size() / 2, 1}};
+        strided_span<const byte, 2> sav2{bytes.data(), bytes.size(), bounds};
+        strided_span<const int, 2> sav3 = sav2.as_strided_span<const int>();
+        CHECK(sav3[0][0] == 0);
+        CHECK(sav3[1][0] == 2);
+        CHECK_THROWS_AS(sav3[1][1], fail_fast);
+        CHECK_THROWS_AS(sav3[0][1], fail_fast);
+    }
+
+    // retype strided array with regular strides - from multi_span
+    {
+        strided_bounds<2> bounds{{2, bytes.size() / 4}, {bytes.size() / 2, 1}};
+        multi_span<const byte, 2, dynamic_range> bytes2 =
+            as_multi_span(bytes, dim<2>(), dim(bytes.size() / 2));
+        strided_span<const byte, 2> sav2{bytes2, bounds};
+        strided_span<int, 2> sav3 = sav2.as_strided_span<int>();
+        CHECK(sav3[0][0] == 0);
+        CHECK(sav3[1][0] == 2);
+        CHECK_THROWS_AS(sav3[1][1], fail_fast);
+        CHECK_THROWS_AS(sav3[0][1], fail_fast);
+    }
+
+    // retype strided array with not enough elements - last dimension of the array is too small
+    {
+        strided_bounds<2> bounds{{4, 2}, {4, 1}};
+        multi_span<const byte, 2, dynamic_range> bytes2 =
+            as_multi_span(bytes, dim<2>(), dim(bytes.size() / 2));
+        strided_span<const byte, 2> sav2{bytes2, bounds};
+        CHECK_THROWS_AS(sav2.as_strided_span<int>(), fail_fast);
+    }
+
+    // retype strided array with not enough elements - strides are too small
+    {
+        strided_bounds<2> bounds{{4, 2}, {2, 1}};
+        multi_span<const byte, 2, dynamic_range> bytes2 =
+            as_multi_span(bytes, dim<2>(), dim(bytes.size() / 2));
+        strided_span<const byte, 2> sav2{bytes2, bounds};
+        CHECK_THROWS_AS(sav2.as_strided_span<int>(), fail_fast);
+    }
+
+    // retype strided array with not enough elements - last dimension does not divide by the new
+    // typesize
+    {
+        strided_bounds<2> bounds{{2, 6}, {4, 1}};
+        multi_span<const byte, 2, dynamic_range> bytes2 =
+            as_multi_span(bytes, dim<2>(), dim(bytes.size() / 2));
+        strided_span<const byte, 2> sav2{bytes2, bounds};
+        CHECK_THROWS_AS(sav2.as_strided_span<int>(), fail_fast);
+    }
+
+    // retype strided array with not enough elements - strides does not divide by the new
+    // typesize
+    {
+        strided_bounds<2> bounds{{2, 1}, {6, 1}};
+        multi_span<const byte, 2, dynamic_range> bytes2 =
+            as_multi_span(bytes, dim<2>(), dim(bytes.size() / 2));
+        strided_span<const byte, 2> sav2{bytes2, bounds};
+        CHECK_THROWS_AS(sav2.as_strided_span<int>(), fail_fast);
+    }
+
+    // retype strided array with irregular strides - from raw data
+    {
+        strided_bounds<1> bounds{bytes.size() / 2, 2};
+        strided_span<const byte, 1> sav2{bytes.data(), bytes.size(), bounds};
+        CHECK_THROWS_AS(sav2.as_strided_span<int>(), fail_fast);
+    }
+
+    // retype strided array with irregular strides - from multi_span
+    {
+        strided_bounds<1> bounds{bytes.size() / 2, 2};
+        strided_span<const byte, 1> sav2{bytes, bounds};
+        CHECK_THROWS_AS(sav2.as_strided_span<int>(), fail_fast);
+    }
+}
+
+TEST_CASE("empty_strided_spans")
+{
+    {
+        multi_span<int, 0> empty_av(nullptr);
+        strided_span<int, 1> empty_sav{empty_av, {0, 1}};
+
+        CHECK(empty_sav.bounds().index_bounds() == index<1>{0});
+        CHECK_THROWS_AS(empty_sav[0], fail_fast);
+        CHECK_THROWS_AS(empty_sav.begin()[0], fail_fast);
+        CHECK_THROWS_AS(empty_sav.cbegin()[0], fail_fast);
+
+        for (const auto& v : empty_sav) {
+            (void) v;
+            CHECK(false);
         }
     }
 
-    TEST(dynamic_strided_span_section_iteration)
     {
-        auto arr = new int[8];
-        for (int i = 0; i < 4; ++i)
-        {
-            arr[2 * i] = 4 + i;
-            arr[2 * i + 1] = i;
-        }
+        strided_span<int, 1> empty_sav{nullptr, 0, {0, 1}};
 
-        auto av = as_multi_span(arr, 8);
+        CHECK(empty_sav.bounds().index_bounds() == index<1>{0});
+        CHECK_THROWS_AS(empty_sav[0], fail_fast);
+        CHECK_THROWS_AS(empty_sav.begin()[0], fail_fast);
+        CHECK_THROWS_AS(empty_sav.cbegin()[0], fail_fast);
+
+        for (const auto& v : empty_sav) {
+            (void) v;
+            CHECK(false);
+        }
+    }
+}
+
+void iterate_every_other_element(multi_span<int, dynamic_range> av)
+{
+    // pick every other element
+
+    auto length = av.size() / 2;
+#if _MSC_VER > 1800
+    auto bounds = strided_bounds<1>({length}, {2});
+#else
+    auto bounds = strided_bounds<1>(index<1>{length}, index<1>{2});
+#endif
+    strided_span<int, 1> strided(&av.data()[1], av.size() - 1, bounds);
+
+    CHECK(strided.size() == length);
+    CHECK(strided.bounds().index_bounds()[0] == length);
+    for (auto i = 0; i < strided.size(); ++i) {
+        CHECK(strided[i] == av[2 * i + 1]);
+    }
+
+    int idx = 0;
+    for (auto num : strided) {
+        CHECK(num == av[2 * idx + 1]);
+        idx++;
+    }
+}
+
+TEST_CASE("strided_span_section_iteration")
+{
+    int arr[8] = {4, 0, 5, 1, 6, 2, 7, 3};
+
+    // static bounds
+    {
+        multi_span<int, 8> av(arr, 8);
         iterate_every_other_element(av);
-
-        delete[] arr;
     }
 
-    void iterate_second_slice(multi_span<int, dynamic_range, dynamic_range, dynamic_range> av)
+    // dynamic bounds
     {
-        int expected[6] = {2,3,10,11,18,19};
-        auto section = av.section({0,1,0}, {3,1,2});
-
-        for (auto i = 0; i < section.extent<0>(); ++i)
-        {
-            for (auto j = 0; j < section.extent<1>(); ++j)
-                for (auto k = 0; k < section.extent<2>(); ++k)
-                {
-                    auto idx = index<3>{i,j,k}; // avoid braces in the CHECK macro
-                    CHECK(section[idx] == expected[2 * i + 2 * j + k]);
-                }
-        }
-
-        for (auto i = 0; i < section.extent<0>(); ++i)
-        {
-            for (auto j = 0; j < section.extent<1>(); ++j)
-                for (auto k = 0; k < section.extent<2>(); ++k)
-                    CHECK(section[i][j][k] == expected[2 * i + 2 * j + k]);
-        }
-
-        int i = 0;
-        for (auto num : section)
-        {
-            CHECK(num == expected[i]);
-            i++;
-        }
-    }
-
-    TEST(strided_span_section_iteration_3d)
-    {
-        int arr[3][4][2];
-        for (auto i = 0; i < 3; ++i)
-        {
-            for (auto j = 0; j < 4; ++j)
-                for (auto k = 0; k < 2; ++k)
-                    arr[i][j][k] = 8 * i + 2 * j + k;
-        }
-
-        {
-            multi_span<int, 3, 4, 2> av = arr;
-            iterate_second_slice(av);
-        }
-    }
-
-    TEST(dynamic_strided_span_section_iteration_3d)
-    {
-        auto height = 12, width = 2;
-        auto size = height * width;
-
-        auto arr = new int[size];
-        for (auto i = 0; i < size; ++i)
-        {
-            arr[i] = i;
-        }
-
-        {
-            auto av = as_multi_span(as_multi_span(arr, 24), dim<3>(), dim<4>(), dim<2>());
-            iterate_second_slice(av);
-        }
-
-        {
-            auto av = as_multi_span(as_multi_span(arr, 24), dim(3), dim<4>(), dim<2>());
-            iterate_second_slice(av);
-        }
-
-        {
-            auto av = as_multi_span(as_multi_span(arr, 24), dim<3>(), dim(4), dim<2>());
-            iterate_second_slice(av);
-        }
-
-        {
-            auto av = as_multi_span(as_multi_span(arr, 24), dim<3>(), dim<4>(), dim(2));
-            iterate_second_slice(av);
-        }
-        delete[] arr;
-    }
-
-    TEST(strided_span_conversion)
-    {
-        // get an multi_span of 'c' values from the list of X's
-
-        struct X { int a; int b; int c; };
-
-        X arr[4] = {{0,1,2},{3,4,5},{6,7,8},{9,10,11}};
-
-        int s = sizeof(int) / sizeof(byte);
-        auto d2 = 3 * s;
-        auto d1 = sizeof(int) * 12 / d2;
-
-        // convert to 4x12 array of bytes
-        auto av = as_multi_span(as_bytes(as_multi_span(arr, 4)), dim(d1), dim(d2));
-
-        CHECK(av.bounds().index_bounds()[0] == 4);
-        CHECK(av.bounds().index_bounds()[1] == 12);
-
-        // get the last 4 columns
-        auto section = av.section({0, 2 * s}, {4, s}); // { { arr[0].c[0], arr[0].c[1], arr[0].c[2], arr[0].c[3] } , { arr[1].c[0], ... } , ... }
-
-                                                       // convert to array 4x1 array of integers
-        auto cs = section.as_strided_span<int>(); // { { arr[0].c }, {arr[1].c } , ... } 
-
-        CHECK(cs.bounds().index_bounds()[0] == 4);
-        CHECK(cs.bounds().index_bounds()[1] == 1);
-
-        // transpose to 1x4 array 
-        strided_bounds<2> reverse_bounds{
-            {cs.bounds().index_bounds()[1] , cs.bounds().index_bounds()[0]},
-            {cs.bounds().strides()[1], cs.bounds().strides()[0]}
-        };
-
-        strided_span<int, 2> transposed{cs.data(), cs.bounds().total_size(), reverse_bounds};
-
-        // slice to get a one-dimensional array of c's
-        strided_span<int, 1> result = transposed[0];
-
-        CHECK(result.bounds().index_bounds()[0] == 4);
-        CHECK_THROW(result.bounds().index_bounds()[1], fail_fast);
-
-        int i = 0;
-        for (auto& num : result)
-        {
-            CHECK(num == arr[i].c);
-            i++;
-        }
-
+        multi_span<int, dynamic_range> av(arr, 8);
+        iterate_every_other_element(av);
     }
 }
 
-int main(int, const char *[])
+TEST_CASE("dynamic_strided_span_section_iteration")
 {
-	return UnitTest::RunAllTests();
+    auto arr = new int[8];
+    for (int i = 0; i < 4; ++i) {
+        arr[2 * i] = 4 + i;
+        arr[2 * i + 1] = i;
+    }
+
+    auto av = as_multi_span(arr, 8);
+    iterate_every_other_element(av);
+
+    delete[] arr;
+}
+
+void iterate_second_slice(multi_span<int, dynamic_range, dynamic_range, dynamic_range> av)
+{
+    const int expected[6] = {2, 3, 10, 11, 18, 19};
+    auto section = av.section({0, 1, 0}, {3, 1, 2});
+
+    for (auto i = 0; i < section.extent<0>(); ++i) {
+        for (auto j = 0; j < section.extent<1>(); ++j)
+            for (auto k = 0; k < section.extent<2>(); ++k) {
+                auto idx = index<3>{i, j, k}; // avoid braces in the CHECK macro
+                CHECK(section[idx] == expected[2 * i + 2 * j + k]);
+            }
+    }
+
+    for (auto i = 0; i < section.extent<0>(); ++i) {
+        for (auto j = 0; j < section.extent<1>(); ++j)
+            for (auto k = 0; k < section.extent<2>(); ++k)
+                CHECK(section[i][j][k] == expected[2 * i + 2 * j + k]);
+    }
+
+    int i = 0;
+    for (const auto num : section) {
+        CHECK(num == expected[i]);
+        i++;
+    }
+}
+
+TEST_CASE("strided_span_section_iteration_3d")
+{
+    int arr[3][4][2]{};
+    for (auto i = 0; i < 3; ++i) {
+        for (auto j = 0; j < 4; ++j)
+            for (auto k = 0; k < 2; ++k) arr[i][j][k] = 8 * i + 2 * j + k;
+    }
+
+    {
+        multi_span<int, 3, 4, 2> av = arr;
+        iterate_second_slice(av);
+    }
+}
+
+TEST_CASE("dynamic_strided_span_section_iteration_3d")
+{
+    const auto height = 12, width = 2;
+    const auto size = height * width;
+
+    auto arr = new int[static_cast<std::size_t>(size)];
+    for (auto i = 0; i < size; ++i) {
+        arr[i] = i;
+    }
+
+    {
+        auto av = as_multi_span(as_multi_span(arr, 24), dim<3>(), dim<4>(), dim<2>());
+        iterate_second_slice(av);
+    }
+
+    {
+        auto av = as_multi_span(as_multi_span(arr, 24), dim(3), dim<4>(), dim<2>());
+        iterate_second_slice(av);
+    }
+
+    {
+        auto av = as_multi_span(as_multi_span(arr, 24), dim<3>(), dim(4), dim<2>());
+        iterate_second_slice(av);
+    }
+
+    {
+        auto av = as_multi_span(as_multi_span(arr, 24), dim<3>(), dim<4>(), dim(2));
+        iterate_second_slice(av);
+    }
+    delete[] arr;
+}
+
+TEST_CASE("strided_span_conversion")
+{
+    // get an multi_span of 'c' values from the list of X's
+
+    struct X
+    {
+        int a;
+        int b;
+        int c;
+    };
+
+    X arr[4] = {{0, 1, 2}, {3, 4, 5}, {6, 7, 8}, {9, 10, 11}};
+
+    int s = sizeof(int) / sizeof(byte);
+    auto d2 = 3 * s;
+    auto d1 = narrow_cast<int>(sizeof(int)) * 12 / d2;
+
+    // convert to 4x12 array of bytes
+    auto av = as_multi_span(as_bytes(as_multi_span(arr, 4)), dim(d1), dim(d2));
+
+    CHECK(av.bounds().index_bounds()[0] == 4);
+    CHECK(av.bounds().index_bounds()[1] == 12);
+
+    // get the last 4 columns
+    auto section = av.section({0, 2 * s}, {4, s}); // { { arr[0].c[0], arr[0].c[1], arr[0].c[2],
+                                                   // arr[0].c[3] } , { arr[1].c[0], ... } , ...
+                                                   // }
+
+    // convert to array 4x1 array of integers
+    auto cs = section.as_strided_span<int>(); // { { arr[0].c }, {arr[1].c } , ... }
+
+    CHECK(cs.bounds().index_bounds()[0] == 4);
+    CHECK(cs.bounds().index_bounds()[1] == 1);
+
+    // transpose to 1x4 array
+    strided_bounds<2> reverse_bounds{
+        {cs.bounds().index_bounds()[1], cs.bounds().index_bounds()[0]},
+        {cs.bounds().strides()[1], cs.bounds().strides()[0]}};
+
+    strided_span<int, 2> transposed{cs.data(), cs.bounds().total_size(), reverse_bounds};
+
+    // slice to get a one-dimensional array of c's
+    strided_span<int, 1> result = transposed[0];
+
+    CHECK(result.bounds().index_bounds()[0] == 4);
+    CHECK_THROWS_AS(result.bounds().index_bounds()[1], fail_fast);
+
+    int i = 0;
+    for (auto& num : result) {
+        CHECK(num == arr[i].c);
+        i++;
+    }
 }
diff --git a/tests/string_span_tests.cpp b/tests/string_span_tests.cpp
index a1cfe79..229a117 100644
--- a/tests/string_span_tests.cpp
+++ b/tests/string_span_tests.cpp
@@ -14,958 +14,950 @@
 //
 ///////////////////////////////////////////////////////////////////////////////
 
-#include <UnitTest++/UnitTest++.h>
-#include <cstdlib>
+#include <catch/catch.hpp>
+
+#include <gsl/gsl> //owner
 #include <gsl/string_span>
-#include <vector>
+
+#include <cstdlib>
 #include <map>
+#include <vector>
 
 using namespace std;
 using namespace gsl;
 
-
-SUITE(string_span_tests)
+TEST_CASE("TestLiteralConstruction")
 {
-
-    TEST(TestLiteralConstruction)
-    {
-        cwstring_span<> v = ensure_z(L"Hello");
-        CHECK(5 == v.length());
+    cwstring_span<> v = ensure_z(L"Hello");
+    CHECK(5 == v.length());
 
 #ifdef CONFIRM_COMPILATION_ERRORS
-        wstring_span<> v2 = ensure0(L"Hello");
+    wstring_span<> v2 = ensure0(L"Hello");
 #endif
-    }
+}
 
-    TEST(TestConstructFromStdString)
+TEST_CASE("TestConstructFromStdString")
+{
+    std::string s = "Hello there world";
+    cstring_span<> v = s;
+    CHECK(v.length() == static_cast<cstring_span<>::index_type>(s.length()));
+}
+
+TEST_CASE("TestConstructFromStdVector")
+{
+    std::vector<char> vec(5, 'h');
+    string_span<> v{vec};
+    CHECK(v.length() == static_cast<string_span<>::index_type>(vec.size()));
+}
+
+TEST_CASE("TestStackArrayConstruction")
+{
+    wchar_t stack_string[] = L"Hello";
+
     {
-        std::string s = "Hello there world";
-        cstring_span<> v = s;
-        CHECK(v.length() == static_cast<cstring_span<>::index_type>(s.length()));
-    }
-
-    TEST(TestConstructFromStdVector)
-    {
-        std::vector<char> vec(5, 'h');
-        string_span<> v {vec};
-        CHECK(v.length() == static_cast<string_span<>::index_type>(vec.size()));
-    }
-
-    TEST(TestStackArrayConstruction)
-    {
-        wchar_t stack_string[] = L"Hello";
-
-        {
-            cwstring_span<> v = ensure_z(stack_string);
-            CHECK(v.length() == 5);
-        }
-
-        {
-            cwstring_span<> v = stack_string;
-            CHECK(v.length() == 5);
-        }
-
-        {
-            wstring_span<> v = ensure_z(stack_string);
-            CHECK(v.length() == 5);
-        }
-
-        {
-            wstring_span<> v = stack_string;
-            CHECK(v.length() == 5);
-        }
-    }
-
-    TEST(TestConstructFromConstCharPointer)
-    {
-        const char* s = "Hello";
-        cstring_span<> v = ensure_z(s);
+        cwstring_span<> v = ensure_z(stack_string);
         CHECK(v.length() == 5);
     }
 
-    TEST(TestConversionToConst)
     {
-        char stack_string[] = "Hello";
-        string_span<> v = ensure_z(stack_string);
-        cstring_span<> v2 = v;
-        CHECK(v.length() == v2.length());
+        cwstring_span<> v = stack_string;
+        CHECK(v.length() == 5);
     }
 
-    TEST(TestConversionFromConst)
     {
-        char stack_string[] = "Hello";
-        cstring_span<> v = ensure_z(stack_string);
-        (void)v;
-#ifdef CONFIRM_COMPILATION_ERRORS
-        string_span<> v2 = v;
-        string_span<> v3 = "Hello";
-#endif
+        wstring_span<> v = ensure_z(stack_string);
+        CHECK(v.length() == 5);
     }
 
-    TEST(TestToString)
     {
-        auto s = gsl::to_string(cstring_span<>{});
-        CHECK(s.length() == 0);
-
-        char stack_string[] = "Hello";
-        cstring_span<> v = ensure_z(stack_string);
-        auto s2 = gsl::to_string(v);
-        CHECK(static_cast<cstring_span<>::index_type>(s2.length()) == v.length());
-        CHECK(s2.length() == 5);
-    }
-
-    TEST(TestToBasicString)
-    {
-        auto s = gsl::to_basic_string<char,std::char_traits<char>,::std::allocator<char>>(cstring_span<>{});
-        CHECK(s.length() == 0);
-
-        char stack_string[] = "Hello";
-        cstring_span<> v = ensure_z(stack_string);
-        auto s2 = gsl::to_basic_string<char,std::char_traits<char>,::std::allocator<char>>(v);
-        CHECK(static_cast<cstring_span<>::index_type>(s2.length()) == v.length());
-        CHECK(s2.length() == 5);
-    }
-    
-    TEST(EqualityAndImplicitConstructors)
-    {
-        {
-            cstring_span<> span = "Hello";
-            cstring_span<> span1;
-
-            // comparison to empty span
-            CHECK(span1 != span);
-            CHECK(span != span1);      
-        }
-
-        {
-            cstring_span<> span = "Hello";
-            cstring_span<> span1 = "Hello1";
-
-            // comparison to different span
-            CHECK(span1 != span);
-            CHECK(span != span1);
-        }
-
-        {
-            cstring_span<> span = "Hello";
-
-            const char ar[] = { 'H', 'e', 'l', 'l', 'o' };
-            const char ar1[] = "Hello";
-            const char ar2[10] = "Hello";
-            const char* ptr = "Hello";
-            const std::string str = "Hello";
-            const std::vector<char> vec = { 'H', 'e', 'l', 'l', 'o' };
-            gsl::span<const char> sp = ensure_z("Hello");
-
-            // comparison to  literal
-            CHECK(span == cstring_span<>("Hello"));
-
-            // comparison to static array with no null termination
-            CHECK(span == cstring_span<>(ar));
-
-            // comparison to static array with null at the end
-            CHECK(span == cstring_span<>(ar1));
-
-            // comparison to static array with null in the middle
-            CHECK(span == cstring_span<>(ar2));
-
-            // comparison to null-terminated c string
-            CHECK(span == cstring_span<>(ptr, 5));
-
-            // comparison to string
-            CHECK(span == cstring_span<>(str));
-
-            // comparison to vector of charaters with no null termination
-            CHECK(span == cstring_span<>(vec));
-
-            // comparison to span
-            CHECK(span == cstring_span<>(sp));
-
-            // comparison to string_span
-            CHECK(span == span);
-        }
-
-        {
-            char ar[] = { 'H', 'e', 'l', 'l', 'o' };
-
-            string_span<> span = ar;
-
-            char ar1[] = "Hello";
-            char ar2[10] = "Hello";
-            char* ptr = ar;
-            std::string str = "Hello";
-            std::vector<char> vec = { 'H', 'e', 'l', 'l', 'o' };
-            gsl::span<char> sp = ensure_z(ar1);
-
-            // comparison to static array with no null termination
-            CHECK(span == string_span<>(ar));
-
-            // comparison to static array with null at the end
-            CHECK(span == string_span<>(ar1));
-
-            // comparison to static array with null in the middle
-            CHECK(span == string_span<>(ar2));
-
-            // comparison to null-terminated c string
-            CHECK(span == string_span<>(ptr, 5));
-
-            // comparison to string
-            CHECK(span == string_span<>(str));
-
-            // comparison to vector of charaters with no null termination
-            CHECK(span == string_span<>(vec));
-
-            // comparison to span
-            CHECK(span == string_span<>(sp));
-
-            // comparison to string_span
-            CHECK(span == span);
-        }
-
-
-        {
-            const char ar[] = { 'H', 'e', 'l', 'l', 'o' };
-            const char ar1[] = "Hello";
-            const char ar2[10] = "Hello";
-            const std::string str = "Hello";
-            const std::vector<char> vec = { 'H', 'e', 'l', 'l', 'o' };
-            gsl::span<const char> sp = ensure_z("Hello");
-
-            cstring_span<> span = "Hello";
-
-            // const span, const other type
-
-            CHECK(span == "Hello");
-            CHECK(span == ar);
-            CHECK(span == ar1);
-            CHECK(span == ar2);
-#ifdef CONFIRM_COMPILATION_ERRORS
-            const char* ptr = "Hello";
-            CHECK(span == ptr);
-#endif
-            CHECK(span == str);
-            CHECK(span == vec);
-            CHECK(span == sp);
-
-            CHECK("Hello" == span);
-            CHECK(ar == span);
-            CHECK(ar1 == span);
-            CHECK(ar2 == span);
-#ifdef CONFIRM_COMPILATION_ERRORS
-            CHECK(ptr == span);
-#endif
-            CHECK(str == span);
-            CHECK(vec == span);
-            CHECK(sp == span);
-
-            // const span, non-const other type
-
-            char _ar[] = { 'H', 'e', 'l', 'l', 'o' };
-            char _ar1[] = "Hello";
-            char _ar2[10] = "Hello";
-            char* _ptr = _ar;
-            std::string _str = "Hello";
-            std::vector<char> _vec = { 'H', 'e', 'l', 'l', 'o' };
-            gsl::span<char> _sp{ _ar, 5 };
-
-            CHECK(span == _ar);
-            CHECK(span == _ar1);
-            CHECK(span == _ar2);
-#ifdef CONFIRM_COMPILATION_ERRORS
-            CHECK(span == _ptr);
-#endif
-            CHECK(span == _str);
-            CHECK(span == _vec);
-            CHECK(span == _sp);
-
-            CHECK(_ar == span);
-            CHECK(_ar1 == span);
-            CHECK(_ar2 == span);
-#ifdef CONFIRM_COMPILATION_ERRORS
-            CHECK(_ptr == span);
-#endif
-            CHECK(_str == span);
-            CHECK(_vec == span);
-            CHECK(_sp == span);
-
-            string_span<> _span{ _ptr, 5 };
-
-            // non-const span, non-const other type
-            
-            CHECK(_span == _ar);
-            CHECK(_span == _ar1);
-            CHECK(_span == _ar2);
-#ifdef CONFIRM_COMPILATION_ERRORS
-            CHECK(_span == _ptr);
-#endif
-            CHECK(_span == _str);
-            CHECK(_span == _vec);
-            CHECK(_span == _sp);
-
-            CHECK(_ar == _span);
-            CHECK(_ar1 == _span);
-            CHECK(_ar2 == _span);
-#ifdef CONFIRM_COMPILATION_ERRORS
-            CHECK(_ptr == _span);
-#endif
-            CHECK(_str == _span);
-            CHECK(_vec == _span);
-            CHECK(_sp == _span);
-
-            // non-const span, const other type
-
-            CHECK(_span == "Hello");
-            CHECK(_span == ar);
-            CHECK(_span == ar1);
-            CHECK(_span == ar2);
-#ifdef CONFIRM_COMPILATION_ERRORS
-            CHECK(_span == ptr);
-#endif
-            CHECK(_span == str);
-            CHECK(_span == vec);
-            CHECK(_span == sp);
-
-            CHECK("Hello" == _span);
-            CHECK(ar == _span);
-            CHECK(ar1 == _span);
-            CHECK(ar2 == _span);
-#ifdef CONFIRM_COMPILATION_ERRORS
-            CHECK(ptr == _span);
-#endif
-            CHECK(str == _span);
-            CHECK(vec == _span);
-            CHECK(sp == _span);
-
-            // two spans
-
-            CHECK(_span == span);
-            CHECK(span == _span);
-        }
-
-        {
-            std::vector<char> str1 = { 'H', 'e', 'l', 'l', 'o' };
-            cstring_span<> span1 = str1;
-            std::vector<char> str2 = std::move(str1);
-            cstring_span<> span2 = str2;
-
-            // comparison of spans from the same vector before and after move (ok)
-            CHECK(span1 == span2);
-        }
-    }
-
-    TEST(ComparisonAndImplicitConstructors)
-    {
-        {
-            cstring_span<> span = "Hello";
-
-            const char ar[] = { 'H', 'e', 'l', 'l', 'o' };
-            const char ar1[] = "Hello";
-            const char ar2[10] = "Hello";
-            const char* ptr = "Hello";
-            const std::string str = "Hello";
-            const std::vector<char> vec = { 'H', 'e', 'l', 'l', 'o' };
-
-            // comparison to  literal
-            CHECK(span < cstring_span<>("Helloo"));
-            CHECK(span > cstring_span<>("Hell"));
-
-            // comparison to static array with no null termination
-            CHECK(span >= cstring_span<>(ar));
-
-            // comparison to static array with null at the end
-            CHECK(span <= cstring_span<>(ar1));
-
-            // comparison to static array with null in the middle
-            CHECK(span >= cstring_span<>(ar2));
-
-            // comparison to null-terminated c string
-            CHECK(span <= cstring_span<>(ptr, 5));
-
-            // comparison to string
-            CHECK(span >= cstring_span<>(str));
-
-            // comparison to vector of charaters with no null termination
-            CHECK(span <= cstring_span<>(vec));
-        }
-
-        {
-            char ar[] = { 'H', 'e', 'l', 'l', 'o' };
-
-            string_span<> span = ar;
-
-            char larr[] = "Hell";
-            char rarr[] = "Helloo";
-
-            char ar1[] = "Hello";
-            char ar2[10] = "Hello";
-            char* ptr = ar;
-            std::string str = "Hello";
-            std::vector<char> vec = { 'H', 'e', 'l', 'l', 'o' };
-
-
-            // comparison to static array with no null termination
-            CHECK(span <= string_span<>(ar));
-            CHECK(span < string_span<>(rarr));
-            CHECK(span > string_span<>(larr));
-
-            // comparison to static array with null at the end
-            CHECK(span >= string_span<>(ar1));
-
-            // comparison to static array with null in the middle
-            CHECK(span <= string_span<>(ar2));
-
-            // comparison to null-terminated c string
-            CHECK(span >= string_span<>(ptr, 5));
-
-            // comparison to string
-            CHECK(span <= string_span<>(str));
-
-            // comparison to vector of charaters with no null termination
-            CHECK(span >= string_span<>(vec));
-        }
-    }
-    TEST(ConstrutorsEnsureZ)
-    {
-        // remove z from literals
-        {
-            cstring_span<> sp = "hello";
-            CHECK((sp.length() == 5));
-        }
-
-        // take the string as is
-        {
-            auto str = std::string("hello");
-            cstring_span<> sp = str;
-            CHECK((sp.length() == 5));
-        }
-
-        // ensure z on c strings
-        {
-            char* ptr = new char[3];
-
-            ptr[0] = 'a';
-            ptr[1] = 'b';
-            ptr[2] = '\0';
-
-            string_span<> span = ensure_z(ptr);
-            CHECK(span.length() == 2);
-
-            delete[] ptr;
-        }
-    }
-
-    TEST(Constructors)
-    {
-        // creating cstring_span
-
-        // from span of a final extent
-        {
-            span<const char, 6> sp = "Hello";
-            cstring_span<> span = sp;
-            CHECK(span.length() == 6);
-        }
-
-        // from const span of a final extent to non-const string_span
-#ifdef CONFIRM_COMPILATION_ERRORS
-        {
-            span<const char, 6> sp = "Hello";
-            string_span<> span = sp;
-            CHECK(span.length() == 6);
-        }
-#endif
-
-        // from string temporary
-#ifdef CONFIRM_COMPILATION_ERRORS
-        {
-            cstring_span<> span = std::string("Hello");
-        }
-#endif
-
-        // default
-        {
-            cstring_span<> span;
-            CHECK(span.length() == 0);
-        }
-
-        // from nullptr
-        {
-            cstring_span<> span(nullptr);
-            CHECK(span.length() == 0);
-        }
-
-        // from string literal
-        {
-            cstring_span<> span = "Hello";
-            CHECK(span.length() == 5);
-        }
-
-        // from const static array
-        {
-            const char ar[] = { 'H', 'e', 'l', 'l', 'o' };
-            cstring_span<> span = ar;
-            CHECK(span.length() == 5);
-        }
-
-        // from non-const static array
-        {
-            char ar[] = { 'H', 'e', 'l', 'l', 'o' };
-            cstring_span<> span = ar;
-            CHECK(span.length() == 5);
-        }
-
-        // from const ptr and length
-        {
-            const char* ptr = "Hello";
-            cstring_span<> span{ ptr, 5 };
-            CHECK(span.length() == 5);
-        }
-
-        // from const ptr and length, include 0
-        {
-            const char* ptr = "Hello";
-            cstring_span<> span{ ptr, 6 };
-            CHECK(span.length() == 6);
-        }
-
-        // from const ptr and length, 0 inside
-        {
-            const char* ptr = "He\0lo";
-            cstring_span<> span{ ptr, 5 };
-            CHECK(span.length() == 5);
-        }
-
-        // from non-const ptr and length
-        {
-            char ar[] = { 'H', 'e', 'l', 'l', 'o' };
-            char* ptr = ar;
-            cstring_span<> span{ ptr, 5 };
-            CHECK(span.length() == 5);
-        }
-
-        // from non-const ptr and length, 0 inside
-        {
-            char ar[] = { 'H', 'e', '\0', 'l', 'o' };
-            char* ptr = ar;
-            cstring_span<> span{ ptr, 5 };
-            CHECK(span.length() == 5);
-        }
-
-        // from const string
-        {
-            const std::string str = "Hello";
-            cstring_span<> span = str;
-            CHECK(span.length() == 5);
-        }
-
-        // from non-const string
-        {
-            std::string str = "Hello";
-            cstring_span<> span = str;
-            CHECK(span.length() == 5);
-        }
-
-        // from const vector
-        {
-            const std::vector<char> vec = { 'H', 'e', 'l', 'l', 'o' };
-            cstring_span<> span = vec;
-            CHECK(span.length() == 5);
-        }
-
-        // from non-const vector
-        {
-            std::vector<char> vec = { 'H', 'e', 'l', 'l', 'o' };
-            cstring_span<> span = vec;
-            CHECK(span.length() == 5);
-        }
-
-        // from const span
-        {
-            std::vector<char> vec = { 'H', 'e', 'l', 'l', 'o' };
-            const span<const char> inner = vec;
-            cstring_span<> span = inner;
-            CHECK(span.length() == 5);
-        }
-
-        // from non-const span
-        {
-            std::vector<char> vec = { 'H', 'e', 'l', 'l', 'o' };
-            span<char> inner = vec;
-            cstring_span<> span = inner;
-            CHECK(span.length() == 5);
-        }
-
-        // from const string_span
-        {
-            std::vector<char> vec = { 'H', 'e', 'l', 'l', 'o' };
-            cstring_span<> tmp = vec;
-            cstring_span<> span = tmp;
-            CHECK(span.length() == 5);
-        }
-
-        // from non-const string_span
-        {
-            std::vector<char> vec = { 'H', 'e', 'l', 'l', 'o' };
-            string_span<> tmp = vec;
-            cstring_span<> span = tmp;
-            CHECK(span.length() == 5);
-        }
-
-        // creating string_span
-
-        // from string literal
-        {
-#ifdef CONFIRM_COMPILATION_ERRORS
-            string_span<> span = "Hello";
-#endif
-        }
-
-        // from const static array
-        {
-#ifdef CONFIRM_COMPILATION_ERRORS
-            const char ar[] = { 'H', 'e', 'l', 'l', 'o' };
-            string_span<> span = ar;
-            CHECK(span.length() == 5);
-#endif
-        }
-
-        // from non-const static array
-        {
-            char ar[] = { 'H', 'e', 'l', 'l', 'o' };
-            string_span<> span = ar;
-            CHECK(span.length() == 5);
-        }
-
-        // from const ptr and length
-        {
-#ifdef CONFIRM_COMPILATION_ERRORS
-            const char* ptr = "Hello";
-            string_span<> span{ ptr, 5 };
-            CHECK(span.length() == 5);
-#endif
-        }
-
-        // from non-const ptr and length
-        {
-            char ar[] = { 'H', 'e', 'l', 'l', 'o' };
-            char* ptr = ar;
-            string_span<> span{ ptr, 5 };
-            CHECK(span.length() == 5);
-        }
-
-        // from const string
-        {
-#ifdef CONFIRM_COMPILATION_ERRORS
-            const std::string str = "Hello";
-            string_span<> span = str;
-            CHECK(span.length() == 5);
-#endif
-        }
-
-        // from non-const string
-        {
-            std::string str = "Hello";
-            string_span<> span = str;
-            CHECK(span.length() == 5);
-        }
-
-        // from const vector
-        {
-#ifdef CONFIRM_COMPILATION_ERRORS
-            const std::vector<char> vec = { 'H', 'e', 'l', 'l', 'o' };
-            string_span<> span = vec;
-            CHECK(span.length() == 5);
-#endif
-        }
-
-        // from non-const vector
-        {
-            std::vector<char> vec = { 'H', 'e', 'l', 'l', 'o' };
-            string_span<> span = vec;
-            CHECK(span.length() == 5);
-        }
-
-        // from const span
-        {
-#ifdef CONFIRM_COMPILATION_ERRORS
-            std::vector<char> vec = { 'H', 'e', 'l', 'l', 'o' };
-            const span<const char> inner = vec;
-            string_span<> span = inner;
-            CHECK(span.length() == 5);
-#endif
-        }
-
-        // from non-const span
-        {
-            std::vector<char> vec = { 'H', 'e', 'l', 'l', 'o' };
-            span<char> inner = vec;
-            string_span<> span = inner;
-            CHECK(span.length() == 5);
-        }
-
-        // from non-const span of non-const data from const vector
-        {
-#ifdef CONFIRM_COMPILATION_ERRORS
-            const std::vector<char> vec = { 'H', 'e', 'l', 'l', 'o' };
-            const span<char> inner = vec;
-            string_span<> span = inner;
-            CHECK(span.length() == 5);
-#endif
-        }
-
-        // from const string_span
-        {
-#ifdef CONFIRM_COMPILATION_ERRORS
-            std::vector<char> vec = { 'H', 'e', 'l', 'l', 'o' };
-            cstring_span<> tmp = vec;
-            string_span<> span = tmp;
-            CHECK(span.length() == 5);
-#endif
-        }
-
-        // from non-const string_span
-        {
-            std::vector<char> vec = { 'H', 'e', 'l', 'l', 'o' };
-            string_span<> tmp = vec;
-            string_span<> span = tmp;
-            CHECK(span.length() == 5);
-        }
-
-        // from non-const string_span from const vector
-        {
-#ifdef CONFIRM_COMPILATION_ERRORS
-            const std::vector<char> vec = { 'H', 'e', 'l', 'l', 'o' };
-            string_span<> tmp = vec;
-            string_span<> span = tmp;
-            CHECK(span.length() == 5);
-#endif
-        }
-
-        // from const string_span of non-const data
-        {
-            std::vector<char> vec = { 'H', 'e', 'l', 'l', 'o' };
-            const string_span<> tmp = vec;
-            string_span<> span = tmp;
-            CHECK(span.length() == 5);
-        }
-    }
-
-    template<typename T>
-    T move_wrapper(T&& t)
-    {
-        return std::move(t);
-    }
-
-    template <class T>
-    T create() { return T{}; }
-
-    template <class T>
-    void use(basic_string_span<T, gsl::dynamic_extent> s) {}
-
-    TEST(MoveConstructors)
-    {
-        // move string_span
-        {
-            cstring_span<> span = "Hello";
-            auto span1 = std::move(span);
-            CHECK(span1.length() == 5);
-        }
-        {
-            cstring_span<> span = "Hello";
-            auto span1 = move_wrapper(std::move(span));
-            CHECK(span1.length() == 5);
-        }
-        {
-            cstring_span<> span = "Hello";
-            auto span1 = move_wrapper(std::move(span));
-            CHECK(span1.length() == 5);
-        }
-
-        // move span
-        {
-            span<const char> span = ensure_z("Hello");
-            cstring_span<> span1 = std::move(span);
-            CHECK(span1.length() == 5);
-        }
-        {
-            span<const char> span = ensure_z("Hello");
-            cstring_span<> span2 = move_wrapper(std::move(span));
-            CHECK(span2.length() == 5);
-        }
-
-        // move string
-        {
-#ifdef CONFIRM_COMPILATION_ERRORS
-            std::string str = "Hello";
-            string_span<> span = std::move(str);
-            CHECK(span.length() == 5);
-#endif
-        }
-        {
-#ifdef CONFIRM_COMPILATION_ERRORS
-            std::string str = "Hello";
-            string_span<> span = move_wrapper<std::string>(std::move(str));
-            CHECK(span.length() == 5);
-#endif
-        }
-        {
-#ifdef CONFIRM_COMPILATION_ERRORS
-            use<char>(create<string>());
-#endif
-        }
-
-        // move container
-        {
-#ifdef CONFIRM_COMPILATION_ERRORS
-            std::vector<char> vec = { 'H', 'e', 'l', 'l', 'o' };
-            string_span<> span = std::move(vec);
-            CHECK(span.length() == 5);
-#endif
-        }
-        {
-#ifdef CONFIRM_COMPILATION_ERRORS
-            std::vector<char> vec = { 'H', 'e', 'l', 'l', 'o' };
-            string_span<> span = move_wrapper<std::vector<char>>(std::move(vec));
-            CHECK(span.length() == 5);
-#endif
-        }
-        {
-#ifdef CONFIRM_COMPILATION_ERRORS
-            use<char>(create<std::vector<char>>());
-#endif
-        }
-    }
-
-    TEST(Conversion)
-    {
-#ifdef CONFIRM_COMPILATION_ERRORS
-        cstring_span<> span = "Hello";
-        cwstring_span<> wspan{ span };
-        CHECK(wspan.length() == 5);
-#endif
-    }
-
-    czstring_span<> CreateTempName(string_span<> span)
-    {
-        Expects(span.size() > 1);
-
-        int last = 0;
-        if (span.size() > 4)
-        {
-            span[0] = 't';
-            span[1] = 'm';
-            span[2] = 'p';
-            last = 3;
-        }
-        span[last] = '\0';
-
-        auto ret = span.subspan(0, 4);
-        return{ ret };
-    }
-
-    TEST(zstring)
-    {
-
-        // create zspan from zero terminated string
-        {
-            char buf[1];
-            buf[0] = '\0';
-
-            zstring_span<> zspan({ buf, 1 });
-
-            CHECK(strlen(zspan.assume_z()) == 0);
-            CHECK(zspan.as_string_span().size() == 0);
-            CHECK(zspan.ensure_z().size() == 0);
-        }
-
-        // create zspan from non-zero terminated string
-        {
-            char buf[1];
-            buf[0] = 'a';
-
-            auto workaround_macro = [&]() { zstring_span<> zspan({ buf, 1 }); };
-            CHECK_THROW(workaround_macro(), fail_fast);
-        }
-
-        // usage scenario: create zero-terminated temp file name and pass to a legacy API
-        {
-            char buf[10];
-
-            auto name = CreateTempName({ buf, 10 });
-            if (!name.empty())
-            {
-                czstring<> str = name.assume_z();
-                CHECK(strlen(str) == 3);
-                CHECK(*(str+3) == '\0');
-            }
-        }
-
-    }
-
-    cwzstring_span<> CreateTempNameW(wstring_span<> span)
-    {
-        Expects(span.size() > 1);
-
-        int last = 0;
-        if (span.size() > 4)
-        {
-            span[0] = L't';
-            span[1] = L'm';
-            span[2] = L'p';
-            last = 3;
-        }
-        span[last] = L'\0';
-
-        auto ret = span.subspan(0, 4);
-        return{ ret };
-    }
-
-    TEST(wzstring)
-    {
-
-        // create zspan from zero terminated string
-        {
-            wchar_t buf[1];
-            buf[0] = L'\0';
-
-            wzstring_span<> zspan({ buf, 1 });
-
-            CHECK(wcsnlen(zspan.assume_z(), 1) == 0);
-            CHECK(zspan.as_string_span().size() == 0);
-            CHECK(zspan.ensure_z().size() == 0);
-        }
-
-        // create zspan from non-zero terminated string
-        {
-            wchar_t buf[1];
-            buf[0] = L'a';
-
-            auto workaround_macro = [&]() { wzstring_span<> zspan({ buf, 1 }); };
-            CHECK_THROW(workaround_macro(), fail_fast);
-        }
-
-        // usage scenario: create zero-terminated temp file name and pass to a legacy API
-        {
-            wchar_t buf[10];
-
-            auto name = CreateTempNameW({ buf, 10 });
-            if (!name.empty())
-            {
-                cwzstring<> str = name.assume_z();
-                CHECK(wcsnlen(str, 10) == 3);
-                CHECK(*(str + 3) == L'\0');
-            }
-        }
-    }
-
-    TEST(Issue305)
-    {
-        std::map<gsl::cstring_span<>, int> foo = { { "foo", 0 },{ "bar", 1 } };
-        CHECK(foo["foo"] == 0);
-        CHECK(foo["bar"] == 1);
+        wstring_span<> v = stack_string;
+        CHECK(v.length() == 5);
     }
 }
 
-int main(int, const char *[])
+TEST_CASE("TestConstructFromConstCharPointer")
 {
-    return UnitTest::RunAllTests();
+    const char* s = "Hello";
+    cstring_span<> v = ensure_z(s);
+    CHECK(v.length() == 5);
+}
+
+TEST_CASE("TestConversionToConst")
+{
+    char stack_string[] = "Hello";
+    string_span<> v = ensure_z(stack_string);
+    cstring_span<> v2 = v;
+    CHECK(v.length() == v2.length());
+}
+
+TEST_CASE("TestConversionFromConst")
+{
+    char stack_string[] = "Hello";
+    cstring_span<> v = ensure_z(stack_string);
+    (void) v;
+#ifdef CONFIRM_COMPILATION_ERRORS
+    string_span<> v2 = v;
+    string_span<> v3 = "Hello";
+#endif
+}
+
+TEST_CASE("TestToString")
+{
+    auto s = gsl::to_string(cstring_span<>{});
+    CHECK(s.length() == 0);
+
+    char stack_string[] = "Hello";
+    cstring_span<> v = ensure_z(stack_string);
+    auto s2 = gsl::to_string(v);
+    CHECK(static_cast<cstring_span<>::index_type>(s2.length()) == v.length());
+    CHECK(s2.length() == 5);
+}
+
+TEST_CASE("TestToBasicString")
+{
+    auto s = gsl::to_basic_string<char, std::char_traits<char>, ::std::allocator<char>>(
+        cstring_span<>{});
+    CHECK(s.length() == 0);
+
+    char stack_string[] = "Hello";
+    cstring_span<> v = ensure_z(stack_string);
+    auto s2 = gsl::to_basic_string<char, std::char_traits<char>, ::std::allocator<char>>(v);
+    CHECK(static_cast<cstring_span<>::index_type>(s2.length()) == v.length());
+    CHECK(s2.length() == 5);
+}
+
+TEST_CASE("EqualityAndImplicitConstructors")
+{
+    {
+        cstring_span<> span = "Hello";
+        cstring_span<> span1;
+
+        // comparison to empty span
+        CHECK(span1 != span);
+        CHECK(span != span1);
+    }
+
+    {
+        cstring_span<> span = "Hello";
+        cstring_span<> span1 = "Hello1";
+
+        // comparison to different span
+        CHECK(span1 != span);
+        CHECK(span != span1);
+    }
+
+    {
+        cstring_span<> span = "Hello";
+
+        const char ar[] = {'H', 'e', 'l', 'l', 'o'};
+        const char ar1[] = "Hello";
+        const char ar2[10] = "Hello";
+        const char* ptr = "Hello";
+        const std::string str = "Hello";
+        const std::vector<char> vec = {'H', 'e', 'l', 'l', 'o'};
+        gsl::span<const char> sp = ensure_z("Hello");
+
+        // comparison to  literal
+        CHECK(span == cstring_span<>("Hello"));
+
+        // comparison to static array with no null termination
+        CHECK(span == cstring_span<>(ar));
+
+        // comparison to static array with null at the end
+        CHECK(span == cstring_span<>(ar1));
+
+        // comparison to static array with null in the middle
+        CHECK(span == cstring_span<>(ar2));
+
+        // comparison to null-terminated c string
+        CHECK(span == cstring_span<>(ptr, 5));
+
+        // comparison to string
+        CHECK(span == cstring_span<>(str));
+
+        // comparison to vector of charaters with no null termination
+        CHECK(span == cstring_span<>(vec));
+
+        // comparison to span
+        CHECK(span == cstring_span<>(sp));
+
+        // comparison to string_span
+        CHECK(span == span);
+    }
+
+    {
+        char ar[] = {'H', 'e', 'l', 'l', 'o'};
+
+        string_span<> span = ar;
+
+        char ar1[] = "Hello";
+        char ar2[10] = "Hello";
+        char* ptr = ar;
+        std::string str = "Hello";
+        std::vector<char> vec = {'H', 'e', 'l', 'l', 'o'};
+        gsl::span<char> sp = ensure_z(ar1);
+
+        // comparison to static array with no null termination
+        CHECK(span == string_span<>(ar));
+
+        // comparison to static array with null at the end
+        CHECK(span == string_span<>(ar1));
+
+        // comparison to static array with null in the middle
+        CHECK(span == string_span<>(ar2));
+
+        // comparison to null-terminated c string
+        CHECK(span == string_span<>(ptr, 5));
+
+        // comparison to string
+        CHECK(span == string_span<>(str));
+
+        // comparison to vector of charaters with no null termination
+        CHECK(span == string_span<>(vec));
+
+        // comparison to span
+        CHECK(span == string_span<>(sp));
+
+        // comparison to string_span
+        CHECK(span == span);
+    }
+
+    {
+        const char ar[] = {'H', 'e', 'l', 'l', 'o'};
+        const char ar1[] = "Hello";
+        const char ar2[10] = "Hello";
+        const std::string str = "Hello";
+        const std::vector<char> vec = {'H', 'e', 'l', 'l', 'o'};
+        const gsl::span<const char> sp = ensure_z("Hello");
+
+        cstring_span<> span = "Hello";
+
+        // const span, const other type
+
+        CHECK(span == "Hello");
+        CHECK(span == ar);
+        CHECK(span == ar1);
+        CHECK(span == ar2);
+#ifdef CONFIRM_COMPILATION_ERRORS
+        const char* ptr = "Hello";
+        CHECK(span == ptr);
+#endif
+        CHECK(span == str);
+        CHECK(span == vec);
+        CHECK(span == sp);
+
+        CHECK("Hello" == span);
+        CHECK(ar == span);
+        CHECK(ar1 == span);
+        CHECK(ar2 == span);
+#ifdef CONFIRM_COMPILATION_ERRORS
+        CHECK(ptr == span);
+#endif
+        CHECK(str == span);
+        CHECK(vec == span);
+        CHECK(sp == span);
+
+        // const span, non-const other type
+
+        char _ar[] = {'H', 'e', 'l', 'l', 'o'};
+        char _ar1[] = "Hello";
+        char _ar2[10] = "Hello";
+        char* _ptr = _ar;
+        std::string _str = "Hello";
+        std::vector<char> _vec = {'H', 'e', 'l', 'l', 'o'};
+        gsl::span<char> _sp{_ar, 5};
+
+        CHECK(span == _ar);
+        CHECK(span == _ar1);
+        CHECK(span == _ar2);
+#ifdef CONFIRM_COMPILATION_ERRORS
+        CHECK(span == _ptr);
+#endif
+        CHECK(span == _str);
+        CHECK(span == _vec);
+        CHECK(span == _sp);
+
+        CHECK(_ar == span);
+        CHECK(_ar1 == span);
+        CHECK(_ar2 == span);
+#ifdef CONFIRM_COMPILATION_ERRORS
+        CHECK(_ptr == span);
+#endif
+        CHECK(_str == span);
+        CHECK(_vec == span);
+        CHECK(_sp == span);
+
+        string_span<> _span{_ptr, 5};
+
+        // non-const span, non-const other type
+
+        CHECK(_span == _ar);
+        CHECK(_span == _ar1);
+        CHECK(_span == _ar2);
+#ifdef CONFIRM_COMPILATION_ERRORS
+        CHECK(_span == _ptr);
+#endif
+        CHECK(_span == _str);
+        CHECK(_span == _vec);
+        CHECK(_span == _sp);
+
+        CHECK(_ar == _span);
+        CHECK(_ar1 == _span);
+        CHECK(_ar2 == _span);
+#ifdef CONFIRM_COMPILATION_ERRORS
+        CHECK(_ptr == _span);
+#endif
+        CHECK(_str == _span);
+        CHECK(_vec == _span);
+        CHECK(_sp == _span);
+
+        // non-const span, const other type
+
+        CHECK(_span == "Hello");
+        CHECK(_span == ar);
+        CHECK(_span == ar1);
+        CHECK(_span == ar2);
+#ifdef CONFIRM_COMPILATION_ERRORS
+        CHECK(_span == ptr);
+#endif
+        CHECK(_span == str);
+        CHECK(_span == vec);
+        CHECK(_span == sp);
+
+        CHECK("Hello" == _span);
+        CHECK(ar == _span);
+        CHECK(ar1 == _span);
+        CHECK(ar2 == _span);
+#ifdef CONFIRM_COMPILATION_ERRORS
+        CHECK(ptr == _span);
+#endif
+        CHECK(str == _span);
+        CHECK(vec == _span);
+        CHECK(sp == _span);
+
+        // two spans
+
+        CHECK(_span == span);
+        CHECK(span == _span);
+    }
+
+    {
+        std::vector<char> str1 = {'H', 'e', 'l', 'l', 'o'};
+        cstring_span<> span1 = str1;
+        std::vector<char> str2 = std::move(str1);
+        cstring_span<> span2 = str2;
+
+        // comparison of spans from the same vector before and after move (ok)
+        CHECK(span1 == span2);
+    }
+}
+
+TEST_CASE("ComparisonAndImplicitConstructors")
+{
+    {
+        cstring_span<> span = "Hello";
+
+        const char ar[] = {'H', 'e', 'l', 'l', 'o'};
+        const char ar1[] = "Hello";
+        const char ar2[10] = "Hello";
+        const char* ptr = "Hello";
+        const std::string str = "Hello";
+        const std::vector<char> vec = {'H', 'e', 'l', 'l', 'o'};
+
+        // comparison to  literal
+        CHECK(span < cstring_span<>("Helloo"));
+        CHECK(span > cstring_span<>("Hell"));
+
+        // comparison to static array with no null termination
+        CHECK(span >= cstring_span<>(ar));
+
+        // comparison to static array with null at the end
+        CHECK(span <= cstring_span<>(ar1));
+
+        // comparison to static array with null in the middle
+        CHECK(span >= cstring_span<>(ar2));
+
+        // comparison to null-terminated c string
+        CHECK(span <= cstring_span<>(ptr, 5));
+
+        // comparison to string
+        CHECK(span >= cstring_span<>(str));
+
+        // comparison to vector of charaters with no null termination
+        CHECK(span <= cstring_span<>(vec));
+    }
+
+    {
+        char ar[] = {'H', 'e', 'l', 'l', 'o'};
+
+        string_span<> span = ar;
+
+        char larr[] = "Hell";
+        char rarr[] = "Helloo";
+
+        char ar1[] = "Hello";
+        char ar2[10] = "Hello";
+        char* ptr = ar;
+        std::string str = "Hello";
+        std::vector<char> vec = {'H', 'e', 'l', 'l', 'o'};
+
+        // comparison to static array with no null termination
+        CHECK(span <= string_span<>(ar));
+        CHECK(span < string_span<>(rarr));
+        CHECK(span > string_span<>(larr));
+
+        // comparison to static array with null at the end
+        CHECK(span >= string_span<>(ar1));
+
+        // comparison to static array with null in the middle
+        CHECK(span <= string_span<>(ar2));
+
+        // comparison to null-terminated c string
+        CHECK(span >= string_span<>(ptr, 5));
+
+        // comparison to string
+        CHECK(span <= string_span<>(str));
+
+        // comparison to vector of charaters with no null termination
+        CHECK(span >= string_span<>(vec));
+    }
+}
+TEST_CASE("ConstrutorsEnsureZ")
+{
+    // remove z from literals
+    {
+        cstring_span<> sp = "hello";
+        CHECK((sp.length() == 5));
+    }
+
+    // take the string as is
+    {
+        auto str = std::string("hello");
+        cstring_span<> sp = str;
+        CHECK((sp.length() == 5));
+    }
+
+    // ensure z on c strings
+    {
+        gsl::owner<char*> ptr = new char[3];
+
+        ptr[0] = 'a';
+        ptr[1] = 'b';
+        ptr[2] = '\0';
+
+        string_span<> span = ensure_z(ptr);
+        CHECK(span.length() == 2);
+
+        delete[] ptr;
+    }
+}
+
+TEST_CASE("Constructors")
+{
+    // creating cstring_span
+
+    // from span of a final extent
+    {
+        span<const char, 6> sp = "Hello";
+        cstring_span<> span = sp;
+        CHECK(span.length() == 6);
+    }
+
+// from const span of a final extent to non-const string_span
+#ifdef CONFIRM_COMPILATION_ERRORS
+    {
+        span<const char, 6> sp = "Hello";
+        string_span<> span = sp;
+        CHECK(span.length() == 6);
+    }
+#endif
+
+// from string temporary
+#ifdef CONFIRM_COMPILATION_ERRORS
+    {
+        cstring_span<> span = std::string("Hello");
+    }
+#endif
+
+    // default
+    {
+        cstring_span<> span;
+        CHECK(span.length() == 0);
+    }
+
+    // from nullptr
+    {
+        cstring_span<> span(nullptr);
+        CHECK(span.length() == 0);
+    }
+
+    // from string literal
+    {
+        cstring_span<> span = "Hello";
+        CHECK(span.length() == 5);
+    }
+
+    // from const static array
+    {
+        const char ar[] = {'H', 'e', 'l', 'l', 'o'};
+        cstring_span<> span = ar;
+        CHECK(span.length() == 5);
+    }
+
+    // from non-const static array
+    {
+        char ar[] = {'H', 'e', 'l', 'l', 'o'};
+        cstring_span<> span = ar;
+        CHECK(span.length() == 5);
+    }
+
+    // from const ptr and length
+    {
+        const char* ptr = "Hello";
+        cstring_span<> span{ptr, 5};
+        CHECK(span.length() == 5);
+    }
+
+    // from const ptr and length, include 0
+    {
+        const char* ptr = "Hello";
+        cstring_span<> span{ptr, 6};
+        CHECK(span.length() == 6);
+    }
+
+    // from const ptr and length, 0 inside
+    {
+        const char* ptr = "He\0lo";
+        cstring_span<> span{ptr, 5};
+        CHECK(span.length() == 5);
+    }
+
+    // from non-const ptr and length
+    {
+        char ar[] = {'H', 'e', 'l', 'l', 'o'};
+        char* ptr = ar;
+        cstring_span<> span{ptr, 5};
+        CHECK(span.length() == 5);
+    }
+
+    // from non-const ptr and length, 0 inside
+    {
+        char ar[] = {'H', 'e', '\0', 'l', 'o'};
+        char* ptr = ar;
+        cstring_span<> span{ptr, 5};
+        CHECK(span.length() == 5);
+    }
+
+    // from const string
+    {
+        const std::string str = "Hello";
+        const cstring_span<> span = str;
+        CHECK(span.length() == 5);
+    }
+
+    // from non-const string
+    {
+        std::string str = "Hello";
+        const cstring_span<> span = str;
+        CHECK(span.length() == 5);
+    }
+
+    // from const vector
+    {
+        const std::vector<char> vec = {'H', 'e', 'l', 'l', 'o'};
+        const cstring_span<> span = vec;
+        CHECK(span.length() == 5);
+    }
+
+    // from non-const vector
+    {
+        std::vector<char> vec = {'H', 'e', 'l', 'l', 'o'};
+        const cstring_span<> span = vec;
+        CHECK(span.length() == 5);
+    }
+
+    // from const span
+    {
+        const std::vector<char> vec = {'H', 'e', 'l', 'l', 'o'};
+        const span<const char> inner = vec;
+        const cstring_span<> span = inner;
+        CHECK(span.length() == 5);
+    }
+
+    // from non-const span
+    {
+        std::vector<char> vec = {'H', 'e', 'l', 'l', 'o'};
+        const span<char> inner = vec;
+        const cstring_span<> span = inner;
+        CHECK(span.length() == 5);
+    }
+
+    // from const string_span
+    {
+        const std::vector<char> vec = {'H', 'e', 'l', 'l', 'o'};
+        const cstring_span<> tmp = vec;
+        const cstring_span<> span = tmp;
+        CHECK(span.length() == 5);
+    }
+
+    // from non-const string_span
+    {
+        std::vector<char> vec = {'H', 'e', 'l', 'l', 'o'};
+        string_span<> tmp = vec;
+        cstring_span<> span = tmp;
+        CHECK(span.length() == 5);
+    }
+
+    // creating string_span
+
+    // from string literal
+    {
+#ifdef CONFIRM_COMPILATION_ERRORS
+        string_span<> span = "Hello";
+#endif
+    }
+
+    // from const static array
+    {
+#ifdef CONFIRM_COMPILATION_ERRORS
+        const char ar[] = {'H', 'e', 'l', 'l', 'o'};
+        string_span<> span = ar;
+        CHECK(span.length() == 5);
+#endif
+    }
+
+    // from non-const static array
+    {
+        char ar[] = {'H', 'e', 'l', 'l', 'o'};
+        string_span<> span = ar;
+        CHECK(span.length() == 5);
+    }
+
+    // from const ptr and length
+    {
+#ifdef CONFIRM_COMPILATION_ERRORS
+        const char* ptr = "Hello";
+        string_span<> span{ptr, 5};
+        CHECK(span.length() == 5);
+#endif
+    }
+
+    // from non-const ptr and length
+    {
+        char ar[] = {'H', 'e', 'l', 'l', 'o'};
+        char* ptr = ar;
+        string_span<> span{ptr, 5};
+        CHECK(span.length() == 5);
+    }
+
+    // from const string
+    {
+#ifdef CONFIRM_COMPILATION_ERRORS
+        const std::string str = "Hello";
+        string_span<> span = str;
+        CHECK(span.length() == 5);
+#endif
+    }
+
+    // from non-const string
+    {
+        std::string str = "Hello";
+        string_span<> span = str;
+        CHECK(span.length() == 5);
+    }
+
+    // from const vector
+    {
+#ifdef CONFIRM_COMPILATION_ERRORS
+        const std::vector<char> vec = {'H', 'e', 'l', 'l', 'o'};
+        string_span<> span = vec;
+        CHECK(span.length() == 5);
+#endif
+    }
+
+    // from non-const vector
+    {
+        std::vector<char> vec = {'H', 'e', 'l', 'l', 'o'};
+        string_span<> span = vec;
+        CHECK(span.length() == 5);
+    }
+
+    // from const span
+    {
+#ifdef CONFIRM_COMPILATION_ERRORS
+        std::vector<char> vec = {'H', 'e', 'l', 'l', 'o'};
+        const span<const char> inner = vec;
+        string_span<> span = inner;
+        CHECK(span.length() == 5);
+#endif
+    }
+
+    // from non-const span
+    {
+        std::vector<char> vec = {'H', 'e', 'l', 'l', 'o'};
+        span<char> inner = vec;
+        string_span<> span = inner;
+        CHECK(span.length() == 5);
+    }
+
+    // from non-const span of non-const data from const vector
+    {
+#ifdef CONFIRM_COMPILATION_ERRORS
+        const std::vector<char> vec = {'H', 'e', 'l', 'l', 'o'};
+        const span<char> inner = vec;
+        string_span<> span = inner;
+        CHECK(span.length() == 5);
+#endif
+    }
+
+    // from const string_span
+    {
+#ifdef CONFIRM_COMPILATION_ERRORS
+        std::vector<char> vec = {'H', 'e', 'l', 'l', 'o'};
+        cstring_span<> tmp = vec;
+        string_span<> span = tmp;
+        CHECK(span.length() == 5);
+#endif
+    }
+
+    // from non-const string_span
+    {
+        std::vector<char> vec = {'H', 'e', 'l', 'l', 'o'};
+        const string_span<> tmp = vec;
+        const string_span<> span = tmp;
+        CHECK(span.length() == 5);
+    }
+
+    // from non-const string_span from const vector
+    {
+#ifdef CONFIRM_COMPILATION_ERRORS
+        const std::vector<char> vec = {'H', 'e', 'l', 'l', 'o'};
+        string_span<> tmp = vec;
+        string_span<> span = tmp;
+        CHECK(span.length() == 5);
+#endif
+    }
+
+    // from const string_span of non-const data
+    {
+        std::vector<char> vec = {'H', 'e', 'l', 'l', 'o'};
+        const string_span<> tmp = vec;
+        const string_span<> span = tmp;
+        CHECK(span.length() == 5);
+    }
+}
+
+template <typename T>
+T move_wrapper(T && t)
+{
+    return std::move(t);
+}
+
+template <class T>
+T create()
+{
+    return T{};
+}
+
+template <class T>
+void use(basic_string_span<T, gsl::dynamic_extent>)
+{
+}
+
+TEST_CASE("MoveConstructors")
+{
+    // move string_span
+    {
+        cstring_span<> span = "Hello";
+        const auto span1 = std::move(span);
+        CHECK(span1.length() == 5);
+    }
+    {
+        cstring_span<> span = "Hello";
+        const auto span1 = move_wrapper(std::move(span));
+        CHECK(span1.length() == 5);
+    }
+    {
+        cstring_span<> span = "Hello";
+        const auto span1 = move_wrapper(std::move(span));
+        CHECK(span1.length() == 5);
+    }
+
+    // move span
+    {
+        span<const char> span = ensure_z("Hello");
+        const cstring_span<> span1 = std::move(span);
+        CHECK(span1.length() == 5);
+    }
+    {
+        span<const char> span = ensure_z("Hello");
+        const cstring_span<> span2 = move_wrapper(std::move(span));
+        CHECK(span2.length() == 5);
+    }
+
+    // move string
+    {
+#ifdef CONFIRM_COMPILATION_ERRORS
+        std::string str = "Hello";
+        string_span<> span = std::move(str);
+        CHECK(span.length() == 5);
+#endif
+    }
+    {
+#ifdef CONFIRM_COMPILATION_ERRORS
+        std::string str = "Hello";
+        string_span<> span = move_wrapper<std::string>(std::move(str));
+        CHECK(span.length() == 5);
+#endif
+    }
+    {
+#ifdef CONFIRM_COMPILATION_ERRORS
+        use<char>(create<string>());
+#endif
+    }
+
+    // move container
+    {
+#ifdef CONFIRM_COMPILATION_ERRORS
+        std::vector<char> vec = {'H', 'e', 'l', 'l', 'o'};
+        string_span<> span = std::move(vec);
+        CHECK(span.length() == 5);
+#endif
+    }
+    {
+#ifdef CONFIRM_COMPILATION_ERRORS
+        std::vector<char> vec = {'H', 'e', 'l', 'l', 'o'};
+        string_span<> span = move_wrapper<std::vector<char>>(std::move(vec));
+        CHECK(span.length() == 5);
+#endif
+    }
+    {
+#ifdef CONFIRM_COMPILATION_ERRORS
+        use<char>(create<std::vector<char>>());
+#endif
+    }
+}
+
+TEST_CASE("Conversion")
+{
+#ifdef CONFIRM_COMPILATION_ERRORS
+    cstring_span<> span = "Hello";
+    cwstring_span<> wspan{span};
+    CHECK(wspan.length() == 5);
+#endif
+}
+
+czstring_span<> CreateTempName(string_span<> span)
+{
+    Expects(span.size() > 1);
+
+    int last = 0;
+    if (span.size() > 4) {
+        span[0] = 't';
+        span[1] = 'm';
+        span[2] = 'p';
+        last = 3;
+    }
+    span[last] = '\0';
+
+    auto ret = span.subspan(0, 4);
+    return {ret};
+}
+
+TEST_CASE("zstring")
+{
+
+    // create zspan from zero terminated string
+    {
+        char buf[1];
+        buf[0] = '\0';
+
+        zstring_span<> zspan({buf, 1});
+
+        CHECK(strlen(zspan.assume_z()) == 0);
+        CHECK(zspan.as_string_span().size() == 0);
+        CHECK(zspan.ensure_z().size() == 0);
+    }
+
+    // create zspan from non-zero terminated string
+    {
+        char buf[1];
+        buf[0] = 'a';
+
+        auto workaround_macro = [&]() { zstring_span<> zspan({buf, 1}); };
+        CHECK_THROWS_AS(workaround_macro(), fail_fast);
+    }
+
+    // usage scenario: create zero-terminated temp file name and pass to a legacy API
+    {
+        char buf[10];
+
+        auto name = CreateTempName({buf, 10});
+        if (!name.empty()) {
+            czstring<> str = name.assume_z();
+            CHECK(strlen(str) == 3);
+            CHECK(*(str + 3) == '\0');
+        }
+    }
+}
+
+cwzstring_span<> CreateTempNameW(wstring_span<> span)
+{
+    Expects(span.size() > 1);
+
+    int last = 0;
+    if (span.size() > 4) {
+        span[0] = L't';
+        span[1] = L'm';
+        span[2] = L'p';
+        last = 3;
+    }
+    span[last] = L'\0';
+
+    auto ret = span.subspan(0, 4);
+    return {ret};
+}
+
+TEST_CASE("wzstring")
+{
+
+    // create zspan from zero terminated string
+    {
+        wchar_t buf[1];
+        buf[0] = L'\0';
+
+        wzstring_span<> zspan({buf, 1});
+
+        CHECK(wcsnlen(zspan.assume_z(), 1) == 0);
+        CHECK(zspan.as_string_span().size() == 0);
+        CHECK(zspan.ensure_z().size() == 0);
+    }
+
+    // create zspan from non-zero terminated string
+    {
+        wchar_t buf[1];
+        buf[0] = L'a';
+
+        const auto workaround_macro = [&]() { wzstring_span<> zspan({buf, 1}); };
+        CHECK_THROWS_AS(workaround_macro(), fail_fast);
+    }
+
+    // usage scenario: create zero-terminated temp file name and pass to a legacy API
+    {
+        wchar_t buf[10];
+
+        const auto name = CreateTempNameW({buf, 10});
+        if (!name.empty()) {
+            cwzstring<> str = name.assume_z();
+            CHECK(wcsnlen(str, 10) == 3);
+            CHECK(*(str + 3) == L'\0');
+        }
+    }
+}
+
+TEST_CASE("Issue305")
+{
+    std::map<gsl::cstring_span<>, int> foo = {{"foo", 0}, {"bar", 1}};
+    CHECK(foo["foo"] == 0);
+    CHECK(foo["bar"] == 1);
 }
diff --git a/tests/test.cpp b/tests/test.cpp
new file mode 100644
index 0000000..bae194d
--- /dev/null
+++ b/tests/test.cpp
@@ -0,0 +1,18 @@
+///////////////////////////////////////////////////////////////////////////////
+//
+// Copyright (c) 2015 Microsoft Corporation. All rights reserved.
+//
+// This code is licensed under the MIT License (MIT).
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+// THE SOFTWARE.
+//
+///////////////////////////////////////////////////////////////////////////////
+
+#define CATCH_CONFIG_MAIN
+#include <catch/catch.hpp>
diff --git a/tests/unittest-cpp b/tests/unittest-cpp
deleted file mode 160000
index dc6b908..0000000
--- a/tests/unittest-cpp
+++ /dev/null
@@ -1 +0,0 @@
-Subproject commit dc6b90838014ab985bf3cd74ac17ad9d00e1fbcb
diff --git a/tests/utils_tests.cpp b/tests/utils_tests.cpp
index 9f4ba02..67cceb5 100644
--- a/tests/utils_tests.cpp
+++ b/tests/utils_tests.cpp
@@ -14,106 +14,97 @@
 //
 ///////////////////////////////////////////////////////////////////////////////
 
-#include <UnitTest++/UnitTest++.h>
+#include <catch/catch.hpp>
+
 #include <gsl/gsl>
+
 #include <functional>
 
 using namespace gsl;
 
-SUITE(utils_tests)
+void f(int& i) { i += 1; }
+
+TEST_CASE("finally_lambda")
 {
-    void f(int& i)
+    int i = 0;
     {
-        i += 1;
+        auto _ = finally([&]() { f(i); });
+        CHECK(i == 0);
     }
-
-    TEST(finally_lambda)
-    {
-        int i = 0;
-        {
-            auto _ = finally([&]() {f(i);});
-            CHECK(i == 0);
-        }
-        CHECK(i == 1);
-    }
-
-    TEST(finally_lambda_move)
-    {
-        int i = 0;
-        {
-            auto _1 = finally([&]() {f(i);});
-            {
-                auto _2 = std::move(_1);
-                CHECK(i == 0);
-            }
-            CHECK(i == 1);
-            {
-                auto _2 = std::move(_1);
-                CHECK(i == 1);
-            }
-            CHECK(i == 1);
-        }
-        CHECK(i == 1);
-    }
-
-    TEST(finally_function_with_bind)
-    {
-        int i = 0;
-        {
-            auto _ = finally(std::bind(&f, std::ref(i)));
-            CHECK(i == 0);
-        }
-        CHECK(i == 1);
-    }
-
-    int j = 0;
-    void g() { j += 1; };
-    TEST(finally_function_ptr)
-    {
-        j = 0;
-        {
-            auto _ = finally(&g);
-            CHECK(j == 0);
-        }
-        CHECK(j == 1);
-    }
-
-    TEST(narrow_cast)
-    {
-        int n = 120;
-        char c = narrow_cast<char>(n);
-        CHECK(c == 120);
-
-        n = 300;
-        unsigned char uc = narrow_cast<unsigned char>(n);
-        CHECK(uc == 44);
-    }
-
-    TEST(narrow)
-    {
-        int n = 120;
-        char c = narrow<char>(n);
-        CHECK(c == 120);
-
-        n = 300;
-        CHECK_THROW(narrow<char>(n), narrowing_error);
-
-        const auto int32_max = std::numeric_limits<int32_t>::max();
-        const auto int32_min = std::numeric_limits<int32_t>::min();
-
-        CHECK(narrow<uint32_t>(int32_t(0)) == 0);
-        CHECK(narrow<uint32_t>(int32_t(1)) == 1);
-        CHECK(narrow<uint32_t>(int32_max) == static_cast<uint32_t>(int32_max));
-
-        CHECK_THROW(narrow<uint32_t>(int32_t(-1)), narrowing_error);
-        CHECK_THROW(narrow<uint32_t>(int32_min), narrowing_error);
-
-        n = -42;
-        CHECK_THROW(narrow<unsigned>(n), narrowing_error);
-    }
+    CHECK(i == 1);
 }
 
-int main(int, const char *[])
+TEST_CASE("finally_lambda_move")
 {
-    return UnitTest::RunAllTests();
+    int i = 0;
+    {
+        auto _1 = finally([&]() { f(i); });
+        {
+            auto _2 = std::move(_1);
+            CHECK(i == 0);
+        }
+        CHECK(i == 1);
+        {
+            auto _2 = std::move(_1);
+            CHECK(i == 1);
+        }
+        CHECK(i == 1);
+    }
+    CHECK(i == 1);
+}
+
+TEST_CASE("finally_function_with_bind")
+{
+    int i = 0;
+    {
+        auto _ = finally(std::bind(&f, std::ref(i)));
+        CHECK(i == 0);
+    }
+    CHECK(i == 1);
+}
+
+int j = 0;
+void g() { j += 1; }
+TEST_CASE("finally_function_ptr")
+{
+    j = 0;
+    {
+        auto _ = finally(&g);
+        CHECK(j == 0);
+    }
+    CHECK(j == 1);
+}
+
+TEST_CASE("narrow_cast")
+{
+    int n = 120;
+    char c = narrow_cast<char>(n);
+    CHECK(c == 120);
+
+    n = 300;
+    unsigned char uc = narrow_cast<unsigned char>(n);
+    CHECK(uc == 44);
+}
+
+TEST_CASE("narrow")
+{
+    int n = 120;
+    const char c = narrow<char>(n);
+    CHECK(c == 120);
+
+    n = 300;
+    CHECK_THROWS_AS(narrow<char>(n), narrowing_error);
+
+    const auto int32_max = std::numeric_limits<int32_t>::max();
+    const auto int32_min = std::numeric_limits<int32_t>::min();
+
+    CHECK(narrow<uint32_t>(int32_t(0)) == 0);
+    CHECK(narrow<uint32_t>(int32_t(1)) == 1);
+    CHECK(narrow<uint32_t>(int32_max) == static_cast<uint32_t>(int32_max));
+
+    CHECK_THROWS_AS(narrow<uint32_t>(int32_t(-1)), narrowing_error);
+    CHECK_THROWS_AS(narrow<uint32_t>(int32_min), narrowing_error);
+
+    n = -42;
+    CHECK_THROWS_AS(narrow<unsigned>(n), narrowing_error);
 }