Passare un concetto a una funzione


12

Poiché i concetti sono definiti come predicati in fase di compilazione, è anche possibile riutilizzare effettivamente questi predicati per algoritmi in fase di compilazione? Ad esempio, sarebbe possibile verificare se tutti i tipi in una tupla sono conformi a un concetto? Per quanto ho visto, non è possibile in alcun modo passare un concetto a una funzione, il che mi riporta all'utilizzo dei modelli per questi casi.

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

Quello che vorrei fare è qualcosa del genere, quindi non devo avvolgere il concetto continuamente per poterlo usare:

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

C'è un modo per avvicinarsi a questo?


E poi ci sarà una proposta per aggiungere concetti di concetti ... A proposito, all_types()può essere significativamente semplificato usando le espressioni fold ... &&:return (... && Predicate::template test<Ts>());
Evg

@Evg sarebbe fantastico :)
Igor R.

Risposte:


5

C'è un modo per avvicinarsi a questo?

Beh, no, non proprio. Non in C ++ 20. Oggi non esiste alcuna nozione nella lingua di un modello concetto-parametro. Perfino i template variabili non possono essere usati come parametri template. Quindi, se abbiamo un concetto per cominciare, non possiamo evitare di avvolgere.

Ma quello che possiamo fare è scrivere wrapper più semplici. Se accettiamo di usare tratti di tipo "vecchio stile" come predicati, in particolare quelli che si comportano come std::integral_constants, allora possiamo avere definizioni di "concetto" piuttosto concise che possono essere usate come predicati.

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

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

È il massimo che posso ottenere , per quanto posso vedere.


Funzionerebbe declinando in qualche modo una lambda generica come modello di modello? Sembra che un lambda non sia mai un modello sebbene giusto, solo l'operatore di chiamata?
Andreas Loanjoe,

@AndreasLoanjoe - Davvero. Una lambda non è mai un modello. Ma se sei disposto a passare lambdas in giro, allora C ++ 20 ti consente di farlo. Posso aggiungere una variante di questo in pochi minuti.
StoryTeller - Unslander Monica

@AndreasLoanjoe - A pensarci bene, una lambda esce ancora molto dettagliata. Non penso sia un'ottima alternativa. Qui è comunque godbolt.org/z/QSHy8X
Storyteller - Unslander Monica

Spero che aggiungeranno qualcosa di meglio :), ma sì, sembra che questa sia la risposta, solo i tratti del tipo di stile offrono questa funzionalità che i concetti non hanno (ancora).
Andreas Loanjoe,

0

Se il tuo obiettivo è "verificare se tutti i tipi in una tupla sono conformi a un concetto" , puoi fare qualcosa del genere:

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

}

DIMOSTRAZIONE DAL VIVO


Perché il tuo AllSamevariadico? Ogni parametro del modello in un pacchetto introdotto da un vincolo di tipo è già vincolato separatamente.
Davis Herring,

@DavisHerring Non capisco. Intendi il concetto stesso o i parametri del modello in *_foo()?
kanstar,

Voglio dire che il codice che hai funziona se rimuovi l' ...on Tse quello && ...che lo utilizza. (Ovviamente il nome AllSamesarebbe quindi inappropriato, ma non sono sicuro del motivo per cui vorrei esprimere un conteggio in unario come <int,int,int>comunque.)
Davis Herring,

@DavisHerring Quindi il concetto non sarebbe AllSame, ma SameAs(vedi en.cppreference.com/w/cpp/concepts/same_as ) e OP voleva avere un concetto che prende un certo numero variadic di parametri di modello.
kanstar,

Ovviamente lo sarebbe std::same_as. Non credo che la parte variadica fosse il punto: era l' identità (desiderata) variabile del concetto. E il mio punto era che l'aspetto variadico del tuo esempio di concetto era irrilevante per il suo uso (perché i concetti non variadici funzionano già con i pacchetti di parametri del modello).
Davis Herring,
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.