Возврат условного выражения '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>, но мне пришлось прибегнуть к решению С++ 14 или c ++ 11.

Ответы

Ответ 1

Цикл для цикла на основе диапазона может выполнять итерацию для любого выражения 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 };
}

DEMO

Это похоже на то, что предлагает с std::span.


std::initilizer_list<T> оборачивает локальный массив, автоматически созданный из заключенного в скобки инициализатора, и поэтому его нельзя использовать в качестве возвращаемого типа, поскольку в таком случае сохраненные в нем указатели становятся недействительными при выходе из функции.