mpusz / mp-units

The quantities and units library for C++
https://mpusz.github.io/mp-units/
MIT License
994 stars 79 forks source link

std::vector<Q> doesn't satisfy Range concept #584

Closed AnthonyVH closed 1 week ago

AnthonyVH commented 1 week ago

Conversion of an std::vector<mp_units::...> to std::span<mp_units::...> doesn't work. I've tried with both quantity and quantity_point and neither work.

I haven't figured out the exact reason yet, but somehow such a vector doesn't satisfy (clang's) Range concept.

Both implicit conversion as well as a bunch of explicit constructors (e.g. std::span<unit_t>(v.begin(), v.size()) or std::span<unit_t>(v.begin(), v.end())) fail.

Here is a Godbolt link of the failure: https://godbolt.org/z/7oKq8h398. Only the clang output is actually useful, since msvc has an issue finding mp_units, and the GCC output shows no info whatsoever.

Minimal example to reproduce:

#include <span>
#include <vector>

using unit_t = mp_units::quantity<mp_units::iec80000::byte>;

int main()
{
  std::vector<unit_t> const v = { 110 * mp_units::iec80000::byte };
  std::span<unit_t> const s = v;
}

Note that implicit conversion using CTAD (i.e. std::span(v)) works. However, the resulting type however is not std::span<unit_t>, and passing it to a function expecting an std::span<unit_t> results in the following clang output:

<source>:13:3: error: no matching function for call to 'foo'
   13 |   foo(s);
      |   ^~~
<source>:7:6: note: candidate function not viable: no known conversion from 'const span<remove_reference_t<ranges::range_reference_t<const vector<quantity<struct byte{{{{}}}}, double>, allocator<quantity<struct byte{{{{}}}}, double>>> &>>>' to 'span<unit_t>' for 1st argument
    7 | void foo(std::span<unit_t>) { }
      |      ^   ~~~~~~~~~~~~~~~~~
1 error generated.

Clang 18.1.0 error messages for the minimal example code above:

<source>:10:27: error: no viable conversion from 'const std::vector<unit_t>' (aka 'const vector<quantity<mp_units::iec80000::byte>>') to 'const std::span<unit_t>' (aka 'const span<quantity<mp_units::iec80000::byte>>')
   10 |   std::span<unit_t> const s = v;
      |                           ^   ~
/opt/compiler-explorer/gcc-13.2.0/lib/gcc/x86_64-linux-gnu/13.2.0/../../../../include/c++/13.2.0/span:227:7: note: candidate constructor not viable: no known conversion from 'const std::vector<unit_t>' (aka 'const vector<quantity<mp_units::iec80000::byte>>') to 'const span<quantity<struct byte{{{{}}}}>> &' for 1st argument
  227 |       span(const span&) noexcept = default;
      |       ^    ~~~~~~~~~~~
/opt/compiler-explorer/gcc-13.2.0/lib/gcc/x86_64-linux-gnu/13.2.0/../../../../include/c++/13.2.0/span:189:2: note: candidate template ignored: could not match 'type_identity_t<element_type>[_ArrayExtent]' (aka 'mp_units::quantity<struct byte{{{{}}}}>[_ArrayExtent]') against 'const std::vector<unit_t>' (aka 'const vector<quantity<mp_units::iec80000::byte>>')
  189 |         span(type_identity_t<element_type> (&__arr)[_ArrayExtent]) noexcept
      |         ^
/opt/compiler-explorer/gcc-13.2.0/lib/gcc/x86_64-linux-gnu/13.2.0/../../../../include/c++/13.2.0/span:196:2: note: candidate template ignored: could not match 'array' against 'vector'
  196 |         span(array<_Tp, _ArrayExtent>& __arr) noexcept
      |         ^
/opt/compiler-explorer/gcc-13.2.0/lib/gcc/x86_64-linux-gnu/13.2.0/../../../../include/c++/13.2.0/span:203:2: note: candidate template ignored: could not match 'array' against 'vector'
  203 |         span(const array<_Tp, _ArrayExtent>& __arr) noexcept
      |         ^
/opt/compiler-explorer/gcc-13.2.0/lib/gcc/x86_64-linux-gnu/13.2.0/../../../../include/c++/13.2.0/span:215:2: note: candidate template ignored: constraints not satisfied [with _Range = const std::vector<unit_t> &]
  215 |         span(_Range&& __range)
      |         ^
/opt/compiler-explorer/gcc-13.2.0/lib/gcc/x86_64-linux-gnu/13.2.0/../../../../include/c++/13.2.0/span:213:7: note: because '__is_compatible_ref<ranges::range_reference_t<const vector<quantity<struct byte{{{{}}}}, double>, allocator<quantity<struct byte{{{{}}}}, double> > > &> >::value' evaluated to false
  213 |           && __is_compatible_ref<ranges::range_reference_t<_Range>>::value
      |              ^
/opt/compiler-explorer/gcc-13.2.0/lib/gcc/x86_64-linux-gnu/13.2.0/../../../../include/c++/13.2.0/span:235:2: note: candidate template ignored: could not match 'span' against 'vector'
  235 |         span(const span<_OType, _OExtent>& __s) noexcept
      |         ^
1 error generated.
mpusz commented 1 week ago

You can't convert a const vector<T> into the const span<T> no matter what type T is. A proper solution is to convert it to span<const T> if that is your intent.

https://godbolt.org/z/aWshesTob

AnthonyVH commented 1 week ago

Ugh, of course. Well, that was certainly the blooper of the day. Sorry to have wasted your time with this.