Skip to content

Commit

Permalink
span: add lifetimebound attribute
Browse files Browse the repository at this point in the history
See http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2018/p0936r0.pdf for
reference.

This helps to guard against dangling references caused by construction from
temporaries such as:

    Span<const int> sp(std::vector<int>{1,2,3});
  • Loading branch information
theuni authored and furszy committed Jul 6, 2021
1 parent 696e91f commit 26eb256
Showing 1 changed file with 14 additions and 4 deletions.
18 changes: 14 additions & 4 deletions src/span.h
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,16 @@
#define ASSERT_IF_DEBUG(x)
#endif

#if defined(__clang__)
#if __has_attribute(lifetimebound)
#define SPAN_ATTR_LIFETIMEBOUND [[clang::lifetimebound]]
#else
#define SPAN_ATTR_LIFETIMEBOUND
#endif
#else
#define SPAN_ATTR_LIFETIMEBOUND
#endif

/** A Span is an object that can refer to a contiguous sequence of objects.
*
* It implements a subset of C++20's std::span.
Expand Down Expand Up @@ -143,14 +153,14 @@ class Span
* Note that this restriction does not exist when converting arrays or other Spans (see above).
*/
template <typename V>
constexpr Span(V& other,
constexpr Span(V& other SPAN_ATTR_LIFETIMEBOUND,
typename std::enable_if<!is_Span<V>::value &&
std::is_convertible<typename std::remove_pointer<decltype(std::declval<V&>().data())>::type (*)[], C (*)[]>::value &&
std::is_convertible<decltype(std::declval<V&>().size()), std::size_t>::value, std::nullptr_t>::type = nullptr)
: m_data(other.data()), m_size(other.size()){}

template <typename V>
constexpr Span(const V& other,
constexpr Span(const V& other SPAN_ATTR_LIFETIMEBOUND,
typename std::enable_if<!is_Span<V>::value &&
std::is_convertible<typename std::remove_pointer<decltype(std::declval<const V&>().data())>::type (*)[], C (*)[]>::value &&
std::is_convertible<decltype(std::declval<const V&>().size()), std::size_t>::value, std::nullptr_t>::type = nullptr)
Expand Down Expand Up @@ -210,9 +220,9 @@ class Span
/** MakeSpan for arrays: */
template <typename A, int N> Span<A> constexpr MakeSpan(A (&a)[N]) { return Span<A>(a, N); }
/** MakeSpan for temporaries / rvalue references, only supporting const output. */
template <typename V> constexpr auto MakeSpan(V&& v) -> typename std::enable_if<!std::is_lvalue_reference<V>::value, Span<const typename std::remove_pointer<decltype(v.data())>::type>>::type { return std::forward<V>(v); }
template <typename V> constexpr auto MakeSpan(V&& v SPAN_ATTR_LIFETIMEBOUND) -> typename std::enable_if<!std::is_lvalue_reference<V>::value, Span<const typename std::remove_pointer<decltype(v.data())>::type>>::type { return std::forward<V>(v); }
/** MakeSpan for (lvalue) references, supporting mutable output. */
template <typename V> constexpr auto MakeSpan(V& v) -> Span<typename std::remove_pointer<decltype(v.data())>::type> { return v; }
template <typename V> constexpr auto MakeSpan(V& v SPAN_ATTR_LIFETIMEBOUND) -> Span<typename std::remove_pointer<decltype(v.data())>::type> { return v; }

/** Pop the last element off a span, and return a reference to that element. */
template <typename T>
Expand Down

0 comments on commit 26eb256

Please sign in to comment.