Przekazywanie koncepcji do funkcji

12

Skoro pojęcia są zdefiniowane jako predykaty czasu kompilacji, czy możliwe jest również ponowne wykorzystanie tych predykatów dla algorytmów kompilacji? Czy na przykład można sprawdzić, czy wszystkie typy w krotce są zgodne z koncepcją? O ile widziałem, nie można w żaden sposób przekazać koncepcji do funkcji, co prowadzi mnie z powrotem do korzystania z szablonów w tych przypadkach.

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

Chciałbym coś takiego, więc nie muszę cały czas owijać koncepcji, aby móc z niej skorzystać:

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

Czy jest jakiś sposób, aby się do tego zbliżyć?

Andreas Loanjoe
źródło
A potem pojawi się propozycja dodania pojęć pojęć ... BTW, all_types()można znacznie uprościć za pomocą wyrażeń fold ... &&:return (... && Predicate::template test<Ts>());
Evg
@Evg byłoby wspaniale :)
Igor R.

Odpowiedzi:

5

Czy jest jakiś sposób, aby się do tego zbliżyć?

Nie, nie bardzo. Nie w C ++ 20. W dzisiejszym języku nie ma pojęcia o szablonie-parametrze. Nawet szablonów zmiennych nie można używać jako parametrów szablonów. Więc jeśli mamy pomysł na początek, nie możemy uniknąć owijania.

Ale możemy zrobić prostsze opakowania. Jeśli zgodzimy się używać cech typu „starego stylu” jako predykatów, szczególnie tych, które zachowują się jak std::integral_constants, wówczas możemy mieć dość zwięzłe definicje „koncepcyjne”, które mogą być używane jako predykaty.

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

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

O ile mi wiadomo, jest tak dobry, jak to tylko możliwe.

StoryTeller - Unslander Monica
źródło
Czy to zadziałałoby, deklarując ogólną lambda jako szablon szablonu w jakikolwiek sposób? Wygląda na to, że lambda nigdy nie jest szablonem, prawda, tylko operator połączeń?
Andreas Loanjoe
@AndreasLoanjoe - Rzeczywiście. Lambda nigdy nie jest szablonem. Ale jeśli chcesz przekazać lambdas, C ++ 20 pozwala ci to zrobić. Mogę dodać wariant tego za kilka minut.
StoryTeller - Unslander Monica
@AndreasLoanjoe - Po namyśle lambda wciąż wychodzi bardzo gadatliwie. Nie sądzę, że to świetna alternatywa. Oto i tak godbolt.org/z/QSHy8X
StoryTeller - Unslander Monica
Mam nadzieję, że dodadzą coś lepszego :), ale tak, wydaje się, że to jest odpowiedź, tylko cechy typu stylu oferują tę koncepcję funkcjonalności (jeszcze).
Andreas Loanjoe
0

Jeśli Twoim celem jest „sprawdzenie, czy wszystkie typy w krotce są zgodne z koncepcją” , możesz zrobić coś takiego:

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

}

DEMO NA ŻYWO

kanstar
źródło
Dlaczego twój AllSamevariadic? Każdy parametr szablonu w pakiecie wprowadzony przez ograniczenie typu jest już osobno ograniczony.
Davis Herring
@ DavisHerring Nie rozumiem. Masz na myśli samą koncepcję lub parametry szablonu *_foo()?
kanstar
To znaczy, że kod masz prace, jeśli usunąć ...na Tsi && ...że używa go. (Oczywiście nazwa AllSamebyłaby wtedy nieodpowiednia, ale nie jestem pewien, dlaczego i tak chciałbym wyrazić liczbę jednoznaczną <int,int,int>).
Davis Herring
@DavisHerring Wtedy koncepcja nie byłaby, AllSameale SameAs(patrz en.cppreference.com/w/cpp/concepts/same_as ), a OP chciał mieć koncepcję, która wymaga różnych parametrów szablonu.
kanstar
Oczywiście, że tak std::same_as. Nie sądzę, by część variadyczna była istotna: była to (pożądana) zmienna tożsamość pojęcia. Chodziło mi o to, że wariantowy wariant twojego przykładu koncepcyjnego był nieistotny z punktu widzenia jego zastosowania (ponieważ koncepcje nie-variadyczne już działają z pakietami parametrów szablonu).
Davis Herring