Возврат условного `range_expression`
Какой самый эффективный способ итерации по одному из нескольких известных диапазонов на основе некоторого условия?
псевдокод для двоичного условия:
for element in (condition ? range_a : range_b)
// do work
Этот "пример" показывает мое намерение использовать цикл for на основе диапазона, но как std::initializer_list
имеет ссылочную семантику, это не сработает.
constexpr auto some_range(bool c) -> std::initializer_list<int> {
if (c) {
return {1,2};
} else {
return {3, 4, 5};
}
}
bool cond = true; // false
for(auto x : some_range(cond)) {
// things
}
выходы: warning: returning address of local temporary object [-Wreturn-stack-address]
Во время выполнения я мог вернуть std::vector
но это потребовало бы создания нового вектора при каждом вызове:
auto some_range(bool c) -> std::vector<int> {
if (c) {
return {1,2};
} else {
return {3, 4, 5};
}
}
Я мог бы использовать фиксированный размер std::array
из std::optional<int>
но я должен прибегнуть к решению C++14 или C++11.
1 ответ
Циклический цикл for может перебирать любое выражение e
, чей тип класса имеет e.begin()
а также e.end()
функции-члены или не-функции begin(e)
а также end(e)
можно найти через ADL. Следовательно, простой итеративный вид может быть:
#include <cstddef>
template <typename T>
struct view
{
T* p;
std::size_t s;
constexpr T* begin() const { return p; }
constexpr T* end() const { return p + s; }
};
и затем вернул, удерживая указатель на массив с, например, статической продолжительностью хранения:
inline view<const int> conditional_range(bool a)
{
static int ra[] = { 1, 2 };
static int rb[] = { 3, 4, 5 };
if (a) return { ra, 2 };
else return { rb, 3 };
}
Это похоже на то, что C++20 предлагает с std::span
,
std::initilizer_list<T>
переносит локальный массив, созданный автоматически из заключенного в скобки инициализатора, и поэтому не может использоваться в качестве возвращаемого типа, поскольку в таком случае сохраненные в нем указатели становятся недействительными при выходе из функции.