лямбда-параметр для трюка constexpr, затем получить массив со связью - PullRequest
0 голосов
/ 14 июля 2020

Я нашел вариант использования, в котором удобно предоставить лямбду функции и заставить эту функцию вызывать лямбда для получения результата constexpr (мой реальный вариант использования включает передачу лямбда в другую лямбду, где первая лямбда возвращает значение, полученное из шаблона посетителя ... но это не имеет отношения к моему основному вопросу).

Я пытаюсь получить массив «со связью», который затем могу использовать в качестве шаблона параметр для других функций. Я вижу очень странную ошибку, когда я получаю «недопустимая инициализация ссылки типа ...», если я не:

  1. Сделать функцию arrWithLinkage1 функцией шаблона с параметром шаблона выброса ,

или

Укажите явный тип возвращаемого значения для функции arrWithLinkage1, например const array<size_t, 3>& вместо const auto&

Я нашел обходной путь, используя функцию arrWithLinkage2, но меня это беспокоит что я могу войти в область неопределенного поведения. Кто-нибудь знает?

Живой пример: https://onlinegdb.com/By9nDmsyw

РЕДАКТИРОВАТЬ: Благодаря Теду (комментарий ниже), похоже, что основная проблема: use of 'auto' in parameter declaration only available with '-fconcepts-ts' Итак это заставляет меня чувствовать себя более комфортно с моим решением, используя template<typename Lam> ниже, если у кого-то нет причин думать иначе ...?

// Test that array has linkage

template<auto& arr>
void test() {}

// Get array with linkage

template<size_t... vals>
constexpr auto valsToArr = array<size_t, sizeof...(vals)>{ vals... };

// Lambda to array with linkage

template<size_t... Idx>
constexpr auto& arrWithLinkage(auto lam, index_sequence<Idx...>) {
    return valsToArr<lam()[Idx]...>;
}

template<size_t=0> // Only works with this...
constexpr auto& arrWithLinkage1(auto lam) {
    constexpr size_t N = lam().size();
    return arrWithLinkage(lam, make_index_sequence<N>{});
}

template<typename Lam> // A little less gross-looking, but still concerned that this is UDB
constexpr auto& arrWithLinkage2(Lam lam) {
    constexpr size_t N = lam().size();
    return arrWithLinkage(lam, make_index_sequence<N>{});
}

int main()
{
    auto lam = [](){ return array{1, 2, 3}; };
    
    constexpr const array<size_t, 3>& res = arrWithLinkage1(lam);
    test<res>();
    
    return 0;
}

1 Ответ

0 голосов
/ 15 июля 2020

РЕШЕНО:

(Отметил бы комментарий Теда как «решение», если бы он опубликовал его как ответ, спасибо Теду!)

  1. Тед привел меня к осознанию что проблема действительно заключалась в том, что я использовал auto в параметре. Использование template<typename Lam> действительно является приемлемым решением. Из-за того, что я искал указанную выше ошибку и видел, как другие люди делают похожие вещи, я думаю, что решение - это «определенное поведение».

  2. Я придумал гораздо лучший общий подход. Просто сделайте так, чтобы лямбда возвращала ссылку в массив constexpr со связью с самого начала (используя переменную шаблона, которая принимает пакет значений через параметр шаблона).

Живой пример: https://onlinegdb.com/Sy-qx3skw

// Test that array has linkage

template<auto& arr>
void test() {
    for(auto& val : arr)
        cout << val << ", ";
    cout << endl;
}

// Get array with linkage

template<size_t... vals>
constexpr auto valsToArr = array<size_t, sizeof...(vals)>{ vals... };

int main()
{
    // Just make the lambda return a reference to a constexpr array to begin with
    // Avoid the need to convert it to a param pack, then an array template variable
    // in order to obtain an array with linkage, that can be passed to test<>()
    
    auto lam = []() -> auto& { return valsToArr<1, 2, 3>; };
    constexpr auto& res = lam();
    test<res>();
    
    return 0;
}
...