In C++ 1Z con i concetti lite, si può fare questo:
template<class T>
requires std::is_base_of<Foo, T>{}()
void foo(T arg) {
}
sotto l'attuale implementazione (sperimentale). Che è abbastanza pulito e chiaro. Ci può essere un modo per fare qualcosa di simile:
template<derived_from<Foo> T>
void foo(T arg) {
}
ma non ho lavorato fuori. Si può sicuramente fare:
template<derived_from_foo T>
void foo(T arg){
}
dove abbiamo un concetto personalizzato chiamato derived_from_foo
che si applica se e solo se il tipo è derivato da foo
. Quello che non so come fare sono i concetti modello - concetti generati da parametri tipo di modello.
In C++ 14, ecco due metodi. In primo luogo, normale SFINAE:
template<class T,
class=std::enable_if_t<std::is_base_of<Foo, T>{}>
>
void foo(T arg) {
}
qui creiamo un modello che deduce il tipo T
dal suo argomento. Prova quindi a dedurre il suo argomento di tipo secondo dal primo argomento.
Il secondo argomento di tipo non ha nome (quindi class=
), perché lo stiamo solo utilizzando per un test SFINAE.
Il test è enable_if_t<condition>
. enable_if_t<condition>
genera il tipo void
se condition
è vero. Se condition
è falso, fallisce in "il contesto immediato", generando un errore di sostituzione.
SFINAE è "Errore di sostituzione non è un errore" - se il tuo tipo T
genera un errore nel "contesto immediato" della firma del modello di funzione, questo non genera un errore in fase di compilazione, ma produce invece un errore in questo caso il modello di funzione non viene considerato un sovraccarico valido.
"Contesto immediato" è un termine tecnico qui, ma in pratica significa che l'errore deve essere "abbastanza presto" da essere rilevato. Se richiede la compilazione di corpi di funzioni per trovare l'errore, ciò non è nel "contesto immediato".
Ora, questo non è l'unico modo. Personalmente mi piace nascondere il mio codice SFINAE dietro un gloss di rispettabilità. Qui di seguito, io uso tag dispacciamento a "nascondere" il fallimento da qualche altra parte, invece di metterlo destra sulla parte anteriore nella firma della funzione:
template<class T>
struct tag {
using type=T;
constexpr tag(tag const&) = default;
constexpr tag() = default;
template<class U,
class=std::enable_if_t<std::is_base_of<T,U>{}>
>
constexpr tag(tag<U>) {}
};
struct Base{};
struct Derived:Base{};
template<class T>
void foo(T t, tag<Base> = tag<T>{}) {
}
qui si crea un tipo di tag
spedizione, e permette la conversione alla base. tag
ci consente di utilizzare i tipi come valori e utilizzare più normali operazioni C++ su di essi (invece di metaprogrammazione modello simile a <>
s in tutto il luogo).
Diamo quindi foo
un secondo argomento di tipo tag<Base>
, quindi lo costruisco con un tag<T>
. Questo non riesce a compilare se T
non è un tipo derivato da Base
.
live example.
La cosa bella di questa soluzione è che il codice che lo rende non funziona sembra più intuitivo - tag<Unrelated>
non può convertire in tag<Base>
. Ciò, tuttavia, non impedisce che la funzione venga considerata per la risoluzione del sovraccarico, il che può essere un problema.
Un modo con meno piastra caldaia è:
template<class T>
void foo(T t, Base*=(T*)0) {
}
dove si usa il fatto che i puntatori possono essere convertiti sse esiste una relazione derivazione tra di loro.
In C++ 11 (e senza constexpr
supporto), per prima cosa scriviamo un aiutante:
namespace notstd {
template<bool b, class T=void>
using enable_if_t=typename std::enable_if<b,T>::type;
}
poi:
template<class T,
class=notstd::enable_if_t<std::is_base_of<Foo, T>::value>
>
void foo(T arg) {
}
se non ti piace l'aiutante, otteniamo questa brutta in più:
template<class T,
class=typename std::enable_if<std::is_base_of<Foo, T>::value>::type
>
void foo(T arg) {
}
t la seconda tecnica C++ 14 sopra può anche essere tradotta in C++ 11.
È possibile scrivere un alias che fa il test se si desidera: [? Come ho limitare una classe template per alcuni tipi]
template<class U>
using base_test=notstd::enable_if_t<std::is_base_of<Base, U>::value>;
template<class T,
class=base_test<T>
>
void foo(T arg) {
}
correlati a (http://stackoverflow.com/ q/16976720/1708801) –
@ShafikYaghmour Si noti che non è un duplicato. Voglio limitare un modello * function *, mentre la domanda collegata fa riferimento a un modello * class *. – becko
@ShafikYaghmour Non lo avresti chiuso se questo era un duplicato esatto e avevi una risposta sull'altro? – Barry