Как правильно проверить член во время компиляции с помощью boost :: hana?

Я пишу программу моделирования физики, и я хочу сделать следующее: у меня есть адаптированная структура hana, и я хочу проверить, есть ли в этой структуре член с именем "AbsorbsEnergy" во время компиляции, используя:

if constexpr ( ... )

Как правильно это сделать в C ++ 17, который я использую?

Теперь, используя документацию по hana, я придумал следующее:

struct HasAE { double absorbedEnergy };
struct HasNoAE {};

temaplate<typename Cell>
void irelevantFunction(Cell& cell){
    auto has_absorbedEnergy = hana::is_valid(
        [](auto &&p) -> decltype((void) p.absorbedEnergy) {});

    if constexpr(has_absorbedEnergy(cell)) { ... }
}

HasAE cell;
HasNoAE anotherCell;
cell.absorbedEnergy = 42; //value known at runtime

irelevantFunction(cell);
irelevantFunction(anotherCell);

Дело в том, что это прекрасно компилируется с g ++ 7.4.0 и делает то, что я ожидаю, но не компилируется с clang ++ - 8. Выдает ошибку:

constexpr, если условие не является постоянным выражением

Я подозреваю, что это происходит из-за того, что аргумент has_absorbedEnergy - cell не является постоянным выражением. Это можно обойти?


person Mike Spencer    schedule 07.05.2019    source источник
comment
У Yakk есть рабочий пример подъема от локальной переменной к выражению constexpr здесь, кажется актуальным: stackoverflow.com/questions/55975924/   -  person R2RT    schedule 07.05.2019
comment
Если вы можете передать cell по значению и у него есть конструктор копирования constexpr, он будет работать. Неясно, передает ли cell состояние выполнения.   -  person Jason Rice    schedule 16.05.2019
comment
Насколько я понимаю, в ячейке есть конструктор копирования constexpr по умолчанию, но он несет состояние времени выполнения. Я отредактировал вопрос, чтобы конкретно показать, как я собираюсь его использовать.   -  person Mike Spencer    schedule 17.05.2019


Ответы (1)


Кажется, ваша проблема связана с требованием стандарта для выражения в if constexpr быть «контекстно преобразованным постоянным выражением типа bool» (см. этот вопрос). Вы можете обойти это, изменив if constexpr на:

if constexpr (decltype(has_absorbedEnergy(cell)){})

https://wandbox.org/permlink/hmMNLberLJmt0ueJ


В качестве альтернативы вы можете использовать выражение SFINAE для достижения желаемого (см. cppreference.com документация std::void_t):

#include <type_traits>
#include <iostream>

template <typename, typename= std::void_t<>>
struct has_absorbedEnergy : std::false_type {};

template <typename T>
struct has_absorbedEnergy<T,
  std::void_t<decltype(std::declval<T&>().absorbedEnergy)>>
    : std::true_type {};

template <typename Cell>
void irelevantFunction([[maybe_unused]] Cell &cell) {
  if constexpr (has_absorbedEnergy<Cell>::value)
    std::cout << "Has absorbedEnergy\n";
  else
    std::cout << "Does not have absorbedEnergy\n";
}

struct HasAbsorbedEnergy
  { int absorbedEnergy; };

struct DoesNotHaveAbsorbedEnergy
  {};

int main()
{
HasAbsorbedEnergy Has;
DoesNotHaveAbsorbedEnergy DoesNot;

irelevantFunction(Has);
irelevantFunction(DoesNot);
}

https://wandbox.org/permlink/0559JhpVQBOwHC0Z

person metalfox    schedule 07.05.2019
comment
Большое Вам спасибо. Я хотел избежать использования SFINAE, так как он мне не очень удобен для чтения, и мне кажется, что почти любую подобную проблему можно решить с помощью constexpr if. Именно поэтому я задал этот вопрос в первую очередь. Уловка с decltype (...) {} работает как шарм, и связанный с этим вопрос довольно хорошо это объясняет. - person Mike Spencer; 07.05.2019