ETLCPP / etl

Embedded Template Library
https://www.etlcpp.com
MIT License
2.24k stars 392 forks source link

Using etl::make_vector to make a vector of two pairs causes a compile error #931

Closed relativityspace-jsmith closed 3 months ago

relativityspace-jsmith commented 3 months ago

I noticed something odd today when trying to use etl::make_vector. This works:

auto foo = etl::make_vector(
    etl::make_pair("foo", 1),
    etl::make_pair("bar", 2),
    etl::make_pair("baz", 3)
);

but this doesn't

auto foo = etl::make_vector(
    etl::make_pair("foo", 1),
    etl::make_pair("bar", 2)
//    etl::make_pair("baz", 3)
);

(even though it's just commenting out one of the entries).

The compile error looks like:

C:/REDACTED/ext/etl/include/etl/utility.h: In instantiation of 'constexpr etl::pair<T1, T2>::pair(U1&&, U2&&) [with U1 = etl::pair<const char (&)[4], int>; U2 = etl::pair<const char (&)[4], int>; T1 = const char (&)[4]; T2 = int]':
C:/REDACTED/ext/etl/include/etl/vector.h:1363:40:   required from 'constexpr etl::vector<typename etl::common_type< <template-parameter-1-1> >::type, sizeof... (T)> etl::make_vector(T&& ...) [with T = {pair<const char (&)[4], int>, pair<const char (&)[4], int>}; typename common_type< <template-parameter-1-1> >::type = pair<const char (&)[4], int>]'
<the code snippet I pasted>:   required from here
C:/REDACTED/ext/etl/include/etl/utility.h:138:9: error: invalid initialization of reference of type 'const char (&)[4]' from expression of type 'etl::pair<const char (&)[4], int>'
  138 |       : first(etl::forward<U1>(a))
      |         ^~~~~~~~~~~~~~~~~~~~~~~~~~
C:/REDACTED/ext/etl/include/etl/utility.h:139:9: error: cannot convert 'etl::pair<const char (&)[4], int>' to 'int' in initialization
  139 |       , second(etl::forward<U2>(b))
      |         ^~~~~~~~~~~~~~~~~~~~~~~~~~~

I think it has something to do with how etl::make_vector is defined:

  template <typename... T>
  constexpr auto make_vector(T&&... t) -> etl::vector<typename etl::common_type_t<T...>, sizeof...(T)>
  {
    return { { etl::forward<T>(t)... } };
  }

I think that, if the parameter pack etl::forward<T>(t)... expands to exactly two elements, it treats { etl::forward<T>(t)... } as the initializer for a pair type, and then tries to call etl::pair<const char *, int>() passing each of the two pairs as the two constructor arguments. Unfortunately, I don't really understand why it's doing this or how to fix it. Hopefully you have some insight!

multiplemonomials commented 3 months ago

Thanks for the quick fix!