Передача концепции в функцию

12

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

#include <type_traits>

template<typename T>
concept FloatLike = std::is_same_v<T, float>;

struct IsFloat
{
    template<typename U>
    constexpr static bool test()
    {
       return FloatLike<U>;
    }
};


template<typename Predicate, typename... T>
constexpr bool all_types()
{
    return (Predicate::template test<T>() && ...);
}


int main()
{
   static_assert(all_types<IsFloat, float, float>());
   static_assert(!all_types<IsFloat, float, int>());
}

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

template<concept Predicate, typename... T>
constexpr bool all_types()
{
    return (Predicate<T> && ...);
}


int main()
{
   static_assert(all_types<FloatLike, float, float>());
   static_assert(!all_types<FloatLike, float, int>());
}

Есть ли способ приблизиться к этому?

Андреас Ланджо
источник
И тогда будет предложение добавить понятия понятий ... Кстати, all_types()можно значительно упростить использование выражений складывания ... &&:return (... && Predicate::template test<Ts>());
Evg
@Evg это было бы здорово :)
Игорь Р.

Ответы:

5

Есть ли способ приблизиться к этому?

Ну нет, не совсем. Не в C ++ 20. Сегодня в языке нет понятия концептуального параметра-шаблона. Даже переменные шаблоны не могут использоваться в качестве параметров шаблона. Так что, если у вас есть концепция для начала, мы не можем избежать упаковки.

Но то, что мы можем сделать, это написать более простые обертки. Если мы согласимся использовать в качестве предикатов признаки типа «старого стиля», особенно те, которые ведут себя как std::integral_constants, то мы можем получить довольно краткие определения «концептов», которые можно использовать в качестве предикатов.

template<typename T>
using FloatLike = std::is_same<T, float>;

template<template <typename> class Predicate, typename... T>
constexpr bool all_types()
{
    return (Predicate<T>{} && ...);
}

Это настолько хорошо, насколько это возможно , насколько я могу видеть.

Рассказчик - Unslander Monica
источник
Будет ли это работать, если каким-либо образом дешифровать общую лямбду в качестве шаблона? Кажется, что лямбда-это никогда не шаблон, хотя верно, только оператор вызова?
Андреас Ланджо
@AndreasLoanjoe - Действительно. Лямбда никогда не является шаблоном. Но если вы готовы передавать лямбды, C ++ 20 позволяет вам это делать. Я могу добавить вариант этого через несколько минут.
StoryTeller - Unslander Моника
@AndreasLoanjoe - Если подумать, лямбда все еще очень многословна. Я не думаю, что это отличная альтернатива. Вот это все равно godbolt.org/z/QSHy8X
StoryTeller - Unslander Monica
Я надеюсь, что они добавят что-то лучше :), но да, похоже, что это ответ, только черты типа стиля предлагают эту функциональную концепцию (пока).
Андреас Ланджо
0

Если ваша цель - «проверить, все ли типы в кортеже соответствуют концепции» , то вы можете сделать что-то вроде этого:

// concept to check if all types in Ts are the same as T
template<typename T, typename... Ts>
concept AllSame = (std::is_same_v<T,Ts> && ...);

// function only accepts floats as template parameters
template<AllSame<float>... Floats>
constexpr void float_foo()
{
}

// function only accepts ints as template parameters
template<AllSame<int>... Ints>
constexpr void int_foo()
{
}

// function only accepts T as template parameters
template<typename T, AllSame<T>... Ts>
constexpr void foo()
{
}

int main()
{
    int_foo<int, int, int>();
    // int_foo<int, int, double>(); // fails to compile
    float_foo<float, float, float>();
    // float_foo<float, float, int>(); // fails to compile
    foo<int, int, int, int>();
    // foo<int, int, int, float>(); // fails to compile
    foo<double, double, double, double>();
    // foo<double, double, double, int>(); // fails to compile

}

LIVE DEMO

kanstar
источник
Почему ваш AllSameвариадик? Каждый параметр шаблона в пакете, введенный ограничением типа, уже ограничен отдельно.
Дэвис Херринг
@DavisHerring Я не понимаю. Вы имеете в виду саму концепцию или параметры шаблона в *_foo()?
Канстар
Я имею в виду, что ваш код работает, если вы удалите ...on Tsи тот, && ...который использует его. (Очевидно, что имя AllSameбыло бы тогда неуместным, но я не уверен, почему я хотел бы выразить счет в одинарном, как в <int,int,int>любом случае.)
Дэвис Херринг
@DavisHerring Тогда концепция не была бы, AllSameно SameAs(см. En.cppreference.com/w/cpp/concepts/same_as ), и OP хотел иметь концепцию, которая принимает переменное число параметров шаблона.
Канстар
Очевидно, это будет std::same_as. Я не думаю, что вариационная часть была смыслом: это была (желаемая) переменная идентичность концепции. И моя точка зрения заключалась в том, что вариационный аспект вашего примера концепции не имеет отношения к его использованию (потому что не вариадические концепции уже работают с пакетами параметров шаблона).
Дэвис Херринг