mirror of
https://github.com/ETLCPP/etl.git
synced 2026-10-28 18:07:10 +08:00
Changed make_list to take its arguments by forwarding reference (#1528)
make_list was the only make_xxx container factory taking its argument pack by value rather than by forwarding reference, costing an extra move per lvalue argument: each lvalue was copy-constructed into the parameter and then moved into the initializer_list backing array, where the forwarding-reference signature copies it there directly. Rvalue prvalue arguments count the same either way in practice, as copy elision constructs them directly into the by-value parameter. Change the pack to T&&..., matching make_vector, make_forward_list and make_indirect_vector. The deduced reference types decay through common_type, so the element type deduction is unchanged. Adds tests for const/non-const lvalues of the element type (guarding against the forward<T> defect fixed for make_array and make_deque), mixed-type common_type deduction, and an instrumented type counting copy/move constructions, which fails with the by-value signature (one extra move per lvalue argument) and passes with the fix. Also corrects the make_list documentation, which described a make_array-style signature with an explicit element type parameter that never matched the implementation. Verified with clang 22 (C++17): all 93 list tests pass; C++14/20 syntax checks pass. Note: mixed-type lvalue arguments are affected by a separate pre-existing defect in etl::common_type (its two-type specialization recurses with decay<T2> twice instead of decay<T1>, decay<T2>), which already affects the other common_type-based factories and is being fixed separately. Co-authored-by: John Wellbelove <jwellbelove@users.noreply.github.com>
This commit is contained in:
parent
9d92081d1e
commit
62925ec1ad
@ -69,13 +69,15 @@ Defines data as an list of `int`, of length `10`, containing the supplied data.
|
||||
## Make template
|
||||
**C++11 and above**
|
||||
```cpp
|
||||
template <typename T, typename... TValues>
|
||||
constexpr auto make_list(TValues&&... values)
|
||||
template <typename... T>
|
||||
constexpr auto make_list(T&&... t) -> etl::list<typename etl::common_type_t<T...>, sizeof...(T)>
|
||||
```
|
||||
|
||||
The element type is deduced as the common type of the arguments.
|
||||
|
||||
### Example
|
||||
```cpp
|
||||
auto data = etl::make_list<int>(0, 1, 2, 3, 4, 5, 6, 7, 8, 9);
|
||||
auto data = etl::make_list(0, 1, 2, 3, 4, 5, 6, 7, 8, 9);
|
||||
```
|
||||
|
||||
## Member types
|
||||
|
||||
@ -2219,7 +2219,7 @@ namespace etl
|
||||
//*************************************************************************
|
||||
#if ETL_USING_CPP11 && ETL_HAS_INITIALIZER_LIST
|
||||
template <typename... T>
|
||||
constexpr auto make_list(T... t) -> etl::list<typename etl::common_type_t<T...>, sizeof...(T)>
|
||||
constexpr auto make_list(T&&... t) -> etl::list<typename etl::common_type_t<T...>, sizeof...(T)>
|
||||
{
|
||||
return {etl::forward<T>(t)...};
|
||||
}
|
||||
|
||||
@ -2272,5 +2272,110 @@ namespace
|
||||
CHECK_EQUAL(ItemNDC("F"), *itr++);
|
||||
}
|
||||
#endif
|
||||
|
||||
//*************************************************************************
|
||||
#if ETL_HAS_INITIALIZER_LIST
|
||||
// Const-qualified lvalue arguments must be accepted and must not affect the
|
||||
// deduced element type (the deduced reference types decay via common_type).
|
||||
TEST(test_make_list_from_const_lvalues_of_element_type)
|
||||
{
|
||||
static const char static_const_lvalue = 42;
|
||||
const char local_const_lvalue = 43;
|
||||
char mutable_lvalue = 44;
|
||||
|
||||
auto data = etl::make_list(static_const_lvalue, local_const_lvalue, mutable_lvalue);
|
||||
|
||||
CHECK((std::is_same<etl::list<char, 3U>, decltype(data)>::value));
|
||||
|
||||
auto itr = data.begin();
|
||||
CHECK_EQUAL(42, *itr++);
|
||||
CHECK_EQUAL(43, *itr++);
|
||||
CHECK_EQUAL(44, *itr++);
|
||||
}
|
||||
#endif
|
||||
|
||||
//*************************************************************************
|
||||
#if ETL_HAS_INITIALIZER_LIST
|
||||
TEST(test_make_list_common_type_from_mixed_types)
|
||||
{
|
||||
auto data = etl::make_list(1, 2L, 3);
|
||||
|
||||
CHECK((std::is_same<etl::list<long, 3U>, decltype(data)>::value));
|
||||
|
||||
auto itr = data.begin();
|
||||
CHECK_EQUAL(1L, *itr++);
|
||||
CHECK_EQUAL(2L, *itr++);
|
||||
CHECK_EQUAL(3L, *itr++);
|
||||
}
|
||||
#endif
|
||||
|
||||
//*************************************************************************
|
||||
#if ETL_HAS_INITIALIZER_LIST
|
||||
struct CopyMoveCounted
|
||||
{
|
||||
explicit CopyMoveCounted(int value_)
|
||||
: value(value_)
|
||||
{
|
||||
}
|
||||
|
||||
CopyMoveCounted(const CopyMoveCounted& other)
|
||||
: value(other.value)
|
||||
{
|
||||
++copy_count;
|
||||
}
|
||||
|
||||
CopyMoveCounted(CopyMoveCounted&& other)
|
||||
: value(other.value)
|
||||
{
|
||||
++move_count;
|
||||
}
|
||||
|
||||
int value;
|
||||
|
||||
static size_t copy_count;
|
||||
static size_t move_count;
|
||||
};
|
||||
|
||||
size_t CopyMoveCounted::copy_count = 0;
|
||||
size_t CopyMoveCounted::move_count = 0;
|
||||
|
||||
// The arguments must be forwarded into the initializer list, not taken by
|
||||
// value: a by-value parameter pack costs an extra move per argument.
|
||||
// Each element is then copied from the initializer_list backing array into
|
||||
// its list node, which is why lvalue arguments count two copies.
|
||||
TEST(test_make_list_argument_copy_and_move_counts)
|
||||
{
|
||||
const CopyMoveCounted const_lvalue(1);
|
||||
CopyMoveCounted mutable_lvalue(2);
|
||||
|
||||
CopyMoveCounted::copy_count = 0;
|
||||
CopyMoveCounted::move_count = 0;
|
||||
|
||||
auto data = etl::make_list(const_lvalue, mutable_lvalue);
|
||||
|
||||
CHECK_EQUAL(4U, CopyMoveCounted::copy_count);
|
||||
#if ETL_USING_CPP17
|
||||
// The exact move counts rely on guaranteed copy elision. Before C++17 the
|
||||
// returned list may be move-constructed once more, which moves each element,
|
||||
// so only the copy counts are checked there.
|
||||
CHECK_EQUAL(0U, CopyMoveCounted::move_count);
|
||||
#endif
|
||||
|
||||
CopyMoveCounted::copy_count = 0;
|
||||
CopyMoveCounted::move_count = 0;
|
||||
|
||||
auto data2 = etl::make_list(CopyMoveCounted(3));
|
||||
|
||||
CHECK_EQUAL(1U, CopyMoveCounted::copy_count);
|
||||
#if ETL_USING_CPP17
|
||||
CHECK_EQUAL(1U, CopyMoveCounted::move_count);
|
||||
#endif
|
||||
|
||||
auto itr = data.begin();
|
||||
CHECK_EQUAL(1, (*itr++).value);
|
||||
CHECK_EQUAL(2, (*itr).value);
|
||||
CHECK_EQUAL(3, data2.begin()->value);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
} // namespace
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user