title: How to convert a std::vector of unique pointers to a std::span of raw pointers?
body: <p>I have the following function in the interface of some module:</p>
  
  <pre><code>void DoSomething(Span&lt;MyObject *const&gt; objects);
  </code></pre>
  
  <p>, where <code>Span</code> is my simplified implementation of the C++20's <code>std::span</code> template.</p>
  
  <p>This function just iterates over a contiguous sequence of pointers to objects and calls some of their functions, without attempting to modify the pointers (thus the <code>const</code> in the signature).</p>
  
  <p>On the caller's side, I have a <code>std::vector&lt;std::unique_ptr&lt;MyObject&gt;&gt;</code>. And I want to pass that vector to the <code>DoSomething</code> function <strong>without allocating additional memory</strong> (for anything like a temporary <code>std::vector&lt;MyObject*&gt;</code>). I just want to convert an lvalue vector of <code>unique_ptr</code>s to a <code>Span</code> of immutable raw pointers in constant time.</p>
  
  <p>It must be possible, because a <code>std::unique_ptr&lt;T&gt;</code> with a stateless deleter has the same size and alignment as a raw <code>T*</code> pointer, and all it stores inside is nothing but that raw pointer itself. So, bytewise, <code>std::vector&lt;std::unique_ptr&lt;MyObject&gt;&gt;</code> must have the same representation as <code>std::vector&lt;MyObject*&gt;</code> -- thus it must be possible to pass it to a function which expects a <code>Span&lt;MyObject *const&gt;</code>.</p>
  
  <p>My question is:</p>
  
  <ol>
  <li><p>Is such a cast possible with the current proposal of <code>std::span</code> without causing undefined behavior and relying on dirty hacks?</p></li>
  <li><p>If it's not, could it be expected in the following standards (e.g., C++23)?</p></li>
  <li><p>What are the dangers of using a cast that I implemented in my version of <code>Span</code>, using a dirty trick with <code>memcpy</code>? It seems to work fine in practice, but I suppose there might be some undefined behavior in it. If there is, in which cases can that undefined behavior shoot me in the foot on MSVC, GCC or Clang/LLVM, and how exactly? I would be grateful for some real examples of such scenarios, if they are possible.</p></li>
  </ol>
  
  <p>My code goes like this:</p>
  
  <pre><code>namespace detail
  {
    constexpr std::size_t dynamic_extent = static_cast&lt;std::size_t&gt;(-1);
  
    template&lt;typename SourceSmartPointer, typename SpanElement, typename = void&gt;
    struct is_smart_pointer_type_compatible_impl
      : std::false_type
    {
    };
  
    template&lt;typename SourceSmartPointer, typename SpanElement&gt;
    struct is_smart_pointer_type_compatible_impl&lt;SourceSmartPointer, SpanElement,
                                                 decltype((void)(std::declval&lt;SourceSmartPointer&amp;&gt;().get()))&gt;
      : std::conjunction&lt;
          std::is_pointer&lt;SpanElement&gt;,
          std::is_const&lt;SpanElement&gt;,
          std::is_convertible&lt;std::add_pointer_t&lt;decltype(std::declval&lt;SourceSmartPointer&amp;&gt;().get())&gt;,
                              SpanElement*&gt;,
          std::is_same&lt;std::remove_cv_t&lt;std::remove_pointer_t&lt;decltype(std::declval&lt;SourceSmartPointer&amp;&gt;().get())&gt;&gt;,
                       std::remove_cv_t&lt;std::remove_pointer_t&lt;SpanElement&gt;&gt;&gt;,
          std::bool_constant&lt;(sizeof(SourceSmartPointer) == sizeof(SpanElement)) &amp;&amp;
                             (alignof(SourceSmartPointer) == alignof(SpanElement))&gt;&gt;
    {
    };
  
    // Helper type trait which detects whether a contiguous range of smart pointers of the source type
    // can be used to initialize a span of respective immutable raw pointers using a memcpy-based hack.
    template&lt;typename SourceSmartPointer, typename SpanElement&gt;
    struct is_smart_pointer_type_compatible
      : is_smart_pointer_type_compatible_impl&lt;SourceSmartPointer, SpanElement&gt;
    {
    };
  
    template&lt;typename T, typename R&gt;
    inline T* cast_smart_pointer_range_data_to_raw_pointer(R&amp; source_range)
    {
      T* result = nullptr;
  
      auto* source_range_data = std::data(source_range);
      std::memcpy(&amp;result, &amp;source_range_data, sizeof(T*));
  
      return result;
    }
  }
  
  template&lt;typename T, std::size_t Extent = detail::dynamic_extent&gt;
  class Span final
  {
  public:
    // ...
  
    // Non-standard extension.
    // Allows, e.g., to convert `std::vector&lt;std::unique_ptr&lt;Object&gt;&gt;` to `Span&lt;Object *const&gt;`
    // by using the fact that such smart pointers are bytewise equal to the resulting raw pointers;
    // `const` is required on the destination type to ensure that the source smart pointers
    // will be read-only for the users of the resulting Span.
    template&lt;typename R,
             std::enable_if_t&lt;std::conjunction&lt;
               std::bool_constant&lt;(Extent == detail::dynamic_extent)&gt;,
               detail::is_smart_pointer_type_compatible&lt;std::remove_reference_t&lt;decltype(*std::data(std::declval&lt;R&amp;&amp;&gt;()))&gt;, T&gt;,
               detail::is_not_span&lt;R&gt;,
               detail::is_not_std_array&lt;R&gt;,
               std::negation&lt;std::is_array&lt;std::remove_cv_t&lt;std::remove_reference_t&lt;R&gt;&gt;&gt;&gt; &gt;::value, int&gt; = 0&gt;
    constexpr Span(R&amp;&amp; source_range)
      : _data(detail::cast_smart_pointer_range_data_to_raw_pointer&lt;T&gt;(source_range))
      , _size(std::size(source_range))
    {
    }
  
    // ...
  
  private:
    T* _data = nullptr;
    std::size_t _size = 0;
  };
  </code></pre>