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

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

#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>());
}

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


person Andreas Loanjoe    schedule 15.11.2019    source источник
comment
И тогда будет предложение добавить концепты концептов... Кстати, all_types() можно существенно упростить, используя выражения fold ... &&:return (... && Predicate::template test<Ts>());   -  person Evg    schedule 15.11.2019
comment
@Evg было бы здорово :)   -  person Igor R.    schedule 15.11.2019


Ответы (2)


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

Ну, нет, не совсем. Не в С++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>{} && ...);
}

Насколько я понимаю, это настолько хорошо, насколько это возможно.

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

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

// 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

}

РЕАЛЬНАЯ ДЕМО

person kanstar    schedule 25.11.2019
comment
Почему ваш AllSame вариативен? Каждый параметр шаблона в пакете, представленном ограничением типа, уже ограничен отдельно. - person Davis Herring; 26.11.2019
comment
@DavisHerring Я не понимаю. Вы имеете в виду сам концепт или параметры шаблона в *_foo()? - person kanstar; 26.11.2019
comment
Я имею в виду, что код, который у вас есть, работает, если вы удалите ... из Ts и && ..., который его использует. (Очевидно, что тогда имя AllSame было бы неуместным, но я не уверен, почему мне все равно нужно выражать счет в унарном виде как <int,int,int>.) - person Davis Herring; 26.11.2019
comment
@DavisHerring Тогда концепция будет не AllSame, а SameAs (см. en.cppreference.com/ w/cpp/concepts/same_as), и OP хотел иметь концепцию, которая принимает переменное количество параметров шаблона. - person kanstar; 26.11.2019
comment
Очевидно, это будет std::same_as. Я не думаю, что дело было в вариативной части: это была (желаемая) переменная identity концепции. И моя точка зрения заключалась в том, что вариативный аспект вашего концептуального примера не имеет отношения к его использованию (поскольку невариативные концепции уже работают с пакетами параметров шаблона). - person Davis Herring; 26.11.2019
comment
@DavisHerring Теперь я понимаю, что вы имеете в виду, и да, вы правы. Не знал, спасибо за разъяснение :) - person kanstar; 26.11.2019