Использование constexpr-if в общей лямбде для определения типа параметра

У меня следующая проблема: у меня есть иерархия классов с базовым классом и двумя подклассами. Я реализовал resolve_type функция, которая принимает экземпляр базового класса и универсальную лямбду (или аналогичную). Функция разрешает свой тип и передает его лямбда-выражению. В этой лямбде я хотел бы проверить тип столбца в условии constexpr-if, чтобы исключить определенные типы. Я пытался сделать это с помощью функций-членов constexpr в подклассах, которые, к сожалению, не работали.

Код:

class AbstractColumn
{
};

template <typename Type>
class DataColumn : public AbstractColumn
{
public:
constexpr bool is_reference_column() { return false; }

void foo() {}
};

class ReferenceColumn : public AbstractColumn
{
public:
constexpr bool is_reference_column() { return true; }
};

template <typename Functor>
resolve_type(const AbstractColumn & col, const Functor & func);

Использование:

AbstractColumn & col = ...;

...

resolve_type(col, [] (const auto & col)
{
// col could be ReferenceColumn, DataColumn<int>, DataColumn<float>, DataColumn<double>, DataColumn<std::string> ...
if constexpr (!col.is_reference_column()) {
col.foo();
}
});

Ошибка компилятора:

Apple LLVM version 8.1.0 (clang-802.0.42)
error: constexpr if condition is not a constant expression
if constexpr (col.is_reference_column()) {

Я знаю, что я мог бы использовать decltype чтобы получить тип, а затем продолжить использование некоторой магии шаблона, но я надеялся найти что-то более читабельное. Мой проект уже использует boost и его библиотеку hana, поэтому решения также могут использовать эти два. У кого-нибудь есть какие-либо идеи?

2

Решение

Использовать static constexpr метод вместо.
Следует минимальный рабочий пример:

#include<type_traits>

struct A {
static constexpr bool is_reference_column() { return false; }
};

int main() {
[](const auto &col) {
if constexpr(std::decay_t<decltype(col)>::is_reference_column()) {
// ...
}
}(A{});
}

Или просто наследует от std::true_type/std::false_type:

#include<type_traits>

struct A: std::true_type {};

int main() {
[](const auto &col) {
if constexpr(std::decay_t<decltype(col)>::value) {
// ...
}
}(A{});
}

Или используйте шаблон промежуточного класса вместо постоянного переопределения is_reference_column:

#include<type_traits>

template<bool refcol>
struct I {
static constexpr bool is_reference_column = refcol;
};

struct A: I<true> {};

int main() {
[](const auto &col) {
if constexpr(std::decay_t<decltype(col)>::is_reference_column) {
// ...
}
}(A{});
}

Множество альтернатив, но вы не можете просто использовать col в постоянном выражении только потому, что вы объявили его как const ссылка. col это экземпляр времени выполнения типа T, нет никаких шансов, что вы сможете использовать его во время компиляции, как вы пытались это сделать.

2

Другие решения

Я думаю, что вы думаете об этом. Вам не нужна функция-член, чтобы просто определить тип объекта. Вы можете просто посмотреть на тип. Итак, на первый взгляд, это просто:

resolve_type(col, [] (const auto& col)
{
if constexpr (hana::typeid_(col) == hana::type_c<ReferenceColumn>) {
col.foo();
}
});

Еще проще было бы создать набор перегрузки и просто использовать разрешение перегрузки. Здесь есть несколько реализаций такого механизма, особенно легко написать на C ++ 17. Используя это:

resolve_type(col, overload(
[](ReferenceColumn const& ref){
ref.foo();
},
[](auto const& other) {
}));
1

По вопросам рекламы [email protected]