Использование 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
, нет никаких шансов, что вы сможете использовать его во время компиляции, как вы пытались это сделать.
Я думаю, что вы думаете об этом. Вам не нужна функция-член, чтобы просто определить тип объекта. Вы можете просто посмотреть на тип. Итак, на первый взгляд, это просто:
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) {
}));