Pasar un concepto a una función

12

Dado que los conceptos se definen como predicados en tiempo de compilación, ¿también es posible reutilizar estos predicados para algoritmos en tiempo de compilación? Por ejemplo, ¿sería posible verificar si todos los tipos de una tupla se ajustan a un concepto? Por lo que he visto, no es posible pasar un concepto a una función de ninguna manera, lo que me lleva de nuevo a usar plantillas para estos casos.

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

Lo que me gustaría hacer es algo como esto, así que no tengo que envolver el concepto todo el tiempo para poder usarlo:

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

¿Hay alguna forma de acercarse a esto?

Andreas Loanjoe
fuente
Y luego habrá una propuesta para agregar conceptos de conceptos ... Por cierto, all_types()puede simplificarse significativamente usando expresiones de plegado ... &&:return (... && Predicate::template test<Ts>());
Evg
@Evg sería genial :)
Igor R.

Respuestas:

5

¿Hay alguna forma de acercarse a esto?

Bueno, no, en realidad no. No en C ++ 20. Actualmente no existe una noción en el lenguaje de una plantilla concepto-parámetro. Incluso las plantillas variables no se pueden usar como parámetros de plantilla. Entonces, si tenemos un concepto para comenzar, no podemos evitar el ajuste.

Pero lo que podemos hacer es escribir envoltorios más simples. Si aceptamos usar rasgos de tipo "estilo antiguo" como predicados, específicamente aquellos que se comportan como std::integral_constants, entonces podemos tener definiciones de "concepto" bastante concisas que pueden usarse como predicados.

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

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

Es tan bueno como puede llegar , hasta donde puedo ver.

StoryTeller - Unslander Monica
fuente
¿Funcionaría esto al descifrar una lambda genérica como plantilla de alguna forma? Parece que una lambda nunca es una plantilla, ¿verdad? ¿Solo el operador de llamadas?
Andreas Loanjoe
@AndreasLoanjoe - De hecho. Una lambda nunca es una plantilla. Pero si está dispuesto a pasar lambdas, entonces C ++ 20 le permite hacerlo. Puedo agregar una variante de eso en unos minutos.
StoryTeller - Unslander Monica
@AndreasLoanjoe - Pensándolo bien, una lambda todavía sale muy detallada. No creo que sea una gran alternativa. Aquí está de todos modos godbolt.org/z/QSHy8X
StoryTeller - Unslander Monica
Espero que agreguen algo mejor :), pero sí, parece que esta es la respuesta, solo los rasgos de tipo de estilo ofrecen estos conceptos de funcionalidad (todavía).
Andreas Loanjoe
0

Si su objetivo es "verificar si todos los tipos en una tupla se ajustan a un concepto" , puede hacer algo como esto:

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

kanstar
fuente
¿Por qué es tu AllSamevariadic? Cada parámetro de plantilla en un paquete introducido por una restricción de tipo ya está limitado por separado.
Davis Herring
@DavisHerring No entiendo. ¿Te refieres al concepto en sí mismo o los parámetros de plantilla en *_foo()?
kanstar
Quiero decir que el código que tienes funciona si eliminas el ...encendido Tsy el && ...que lo usa. (Obviamente, el nombre AllSamesería entonces inadecuado, pero no estoy seguro de por qué me gustaría expresar un recuento en unario como <int,int,int>de todos modos.)
Davis Arenque
@DavisHerring Entonces el concepto no sería AllSamesino SameAs(ver en.cppreference.com/w/cpp/concepts/same_as ) y OP quería tener un concepto que tomara un número variable de parámetros de plantilla.
kanstar el
Obviamente lo sería std::same_as. No creo que la parte variada fuera el punto: era la identidad variable (deseada) del concepto. Y mi punto fue que el aspecto variado de su ejemplo conceptual era irrelevante para su uso (porque los conceptos no variables ya funcionan con paquetes de parámetros de plantilla).
Davis Herring