Il compilatore cerca sempre di instanti mangiato ogni singolo ramo di std :: condizionale, anche quelli che non sono presi. Per dirla in un altro modo, il cortocircuito non sta accadendo.
std::conditional<B,T,F>
è previsto allo scopo di eseguire una compiletime scelta tra trovati tipiT
e F
, a seconda della boolean B
. La scelta di viene effettuata per specializzazione.Quando B
è vero, la specializzazione istanziato è:
std::conditional<true,T,F>
{
typedef T type;
};
E quando B
è falso, la specializzazione del istanziato è:
std::conditional<false,T,F>
{
typedef F type;
};
Si noti che per istanziare sia specializzazione, sia T
e F
must essere istanziato. Non ci sono "rami". La nozione di "cortocircuito" dell'istanza di std::conditional<true,T,F>
o std::conditional<false,T,F>
potrebbe significare solo che non lo fa.
Quindi no, non è possibile implementare DeferInstantiation<U>
, per il parametro tipo U
, tale che un'esemplificazione di
std::conditional<{true|false},DeferInstantiation<T>,DeferInstantiation<F>>
non comporterà istanziazione di DeferInstantiation<T>
e DeferInstantiation<F>>
, e quindi di T
, e di F
.
Per eseguire una scelta compiletime su quale o due o più modelli sono istanziata, il linguaggio fornisce specializzazione (come appena illustrato nella definizione di std::conditional<B,T,F>
stesso); fornisce il modello di funzione sovraccarico risoluzione e fornisce SFINAE. Specializzazione e sovraccarico di risoluzione può ogni essere sinergicamente sfruttato con SFINAE, attraverso il supporto delle librerie di std::enable_if<B,T>
Il problema che vi ha ostacolato nella realizzazione particolare ricorsiva meta-funzione che si desidera non è uno di scegliere tra data tipi ma di scegliere il modello nel quale dirigere l'istanza ricorsiva. std::conditional
non è per lo scopo. @ La risposta di Pradhan dimostra che un modello diverso da std::conditional
può essere scritto per effettuare una scelta compositime tra due modelli , senza che implica che entrambi devono essere istanziati. Applica la specializzazione per farlo.
Come dici tu, hai già trovato una soluzione di specializzazione per il problema . Questo è in linea di principio il modo giusto per controllare ricorsivamente la selezione del modello in meta-funzioni ricorsive. Tuttavia, con l'avvento di constexpr
, le meta-funzioni ricorsive non comandano nulla come la quota di mercato dei problemi che facevano in precedenza, e la maggior parte del mal di cervello che hanno causato è un ricordo del passato.
Il problema particolare qui - determinare in compiletime se una stringa è una stringa di un altro - possono essere risolti senza prese con template meta-programmazione, e senza rappresentare stringhe compiletime altrimenti che come tradizionali stringhe letterali:
#include <cstddef>
constexpr std::size_t str_len(char const *s)
{
return *s ? 1 + str_len(s + 1) : 0;
}
constexpr bool
is_substr(char const * src, char const *targ,
std::size_t si = 0, std::size_t ti = 0)
{
return !targ[ti] ? true :
str_len(src + si) < str_len(targ + ti) ? false :
src[si] == targ[ti] ?
is_substr(src,targ,si + 1, ti + 1) :
is_substr(src,targ,si + 1, 0);
}
// Compiletime tests...
static_assert(is_substr("",""),"");
static_assert(is_substr("qwerty",""),"");
static_assert(is_substr("qwerty","qwerty"),"");
static_assert(is_substr("qwerty","qwert"),"");
static_assert(is_substr("qwerty","werty"),"");
static_assert(is_substr("qwerty","wert"),"");
static_assert(is_substr("qwerty","er"),"");
static_assert(!is_substr("qwerty","qy"),"");
static_assert(!is_substr("qwerty","et"),"");
static_assert(!is_substr("qwerty","qwertyz"),"");
static_assert(!is_substr("qwerty","pqwerty"),"");
static_assert(!is_substr("","qwerty"),"");
int main()
{
return 0;
}
Questo verrà compilato come C++ 11 o superiore.
Si può anche avere ragioni per desiderare di rappresentare le stringhe compiletime come CharList<char ...>
diversa rendendoli suscettibili di query TMP compiletime come questo. Possiamo vedere che CharList<char ...Cs>
ha una statica membro costante size
valutazione di sizeof...(Cs)
ed ha una funzione at<N>()
membro static valutare la N
esimo del ...Cs
. In tal caso (assumendo che at<N>()
è debug), si potrebbe adattare is_substr
di essere una funzione template aspetta CharList<char ...>
parametri all'incirca le seguenti righe:
#include <type_traits>
template<
class SrcList, class TargList, std::size_t SrcI = 0, std::size_t TargI = 0>
constexpr typename
std::enable_if<(TargI == TargList::size && SrcI <= SrcList::size),bool>::type
is_substr()
{
return true;
}
template<
class SrcList, class TargList, std::size_t SrcI = 0, std::size_t TargI = 0>
constexpr typename
std::enable_if<(TargI < TargList::size && SrcI == SrcList::size),bool>::type
is_substr()
{
return false;
}
template<
class SrcList, class TargList, std::size_t SrcI = 0, std::size_t TargI = 0>
constexpr typename
std::enable_if<(TargI < TargList::size && SrcI < SrcList::size),bool>::type
is_substr()
{
return SrcList::template at<SrcI>() == TargList::template at<TargI>() ?
is_substr<SrcList,TargList,SrcI + 1,TargI + 1>() :
is_substr<SrcList,TargList,SrcI + 1,0>();
}
che illustra l'applicazione di SFINAE, leveraged by std::enable_if
Infine, si potrebbe anche essere interessato a questo programma:
#include <iostream>
template<char const * Arr>
struct string_lit_type
{
static constexpr const char * str = Arr;
static constexpr std::size_t size = str_len(str);
static constexpr char at(std::size_t i) {
return str[i];
}
};
constexpr char arr[] = "Hello World\n";
int main()
{
std::cout << string_lit_type<arr>::str;
std::cout << string_lit_type<arr>::size << std::endl;
std::cout << string_lit_type<arr>::at(0) << std::endl;
return 0;
}
che stampa:
Hello World
12
H
(codice compilato con g ++ 4.9, clang 3.5)