2013-03-18 13 views
17

Mi rendo conto che "perché le cose sono come sono" le domande di solito non sono le migliori, ma ci sono molte persone su SO che sono sintonizzate sulle discussioni standard del comitato, quindi spero che questo possa essere risolto di fatto, poiché sono legittimamente curioso a quale sia la risposta.Perché std :: result_of accetta un tipo di funzione (non correlato) come argomento di tipo?

Fondamentalmente, ho impiegato molto tempo per capire cosa stava succedendo con la firma del modello di std::result_of la prima volta che l'ho visto: ho pensato che fosse un costrutto completamente nuovo per i parametri del modello che non avevo mai visto prima.

template< class F, class... ArgTypes > 
class result_of<F(ArgTypes...)>; 

Dopo qualche tempo a pensarci, ho capito che cosa questo realmente era: F(ArgTypes...) è un tipo di funzione, ma è non il tipo di funzione il cui tipo di risultato è in corso di valutazione (questo è solo F): si tratta di il tipo di una funzione che accetta gli argomenti ArgTypes... e restituisce tipo F.

Non è questo ... strano? Tipo di hackish? Qualcuno sa se la commissione ha mai discusso di alternative, come, per esempio, le seguenti ...

template< class F, class... ArgTypes > 
class result_of<F, ArgTypes...>; 

?

Immagino sia possibile che ci siano situazioni in cui il secondo costrutto non può essere usato facilmente come il primo, ma quali?

Non sto cercando di esprimere un giudizio su questo, ma è solo che questo mi ha legittimamente confuso la prima volta che l'ho visto, quindi sono curioso di sapere se c'è una buona ragione per farlo. Mi rendo conto che parte della risposta potrebbe essere semplicemente "perché Boost ha fatto" in questo modo, ma ancora che lasciano le restanti domande (di fatto) ...

  • C'è una ragione tecnica Boost scegliere questa sintassi per codificare tipo informazione piuttosto che qualche alternativa?

  • si sono avute discussioni da parte del comitato C++ 11 su come appropriata era di standardizzare questo, dato che std::result_of può essere implementato in termini di decltype abbastanza facilmente in ogni caso?

risposta

16

Avere una funzione di tipo come parametro permette di avere un modello di "variadic" classe senza restrizioni, anche in C++ 03. Pensaci: in C++ 03 non disponevamo di modelli variadici. E non è possibile "sovraccaricare" un modello di classe come è possibile con i modelli di funzione, quindi come sarebbe altrimenti possibile consentire quantità diverse di "argomenti" alla funzione?

Usando un tipo di funzione, si può semplicemente aggiungere eventuali specializzazioni parziali numero per il diverso numero di parametri:

template<class Fty> 
struct result_of; 

template<class F> 
struct result_of<F()>{ /*...*/ }; 

template<class F, class A0> 
struct result_of<F(A0)>{ /*...*/ }; 

template<class F, class A0, class A1> 
struct result_of<F(A0, A1)>{ /*...*/ }; 

// ... 

L'unico altro modo per fare questo in C++ 03 è argomenti di template predefiniti e parzialmente specializzata per ogni caso - lo svantaggio è che non assomiglia più a una chiamata di funzione e che qualsiasi tipo di wrapper che utilizza internamente result_of non può semplicemente passare Sig.


Ora, c'è un inconveniente con il modo in cui la funzione di tipo - si ottiene anche tutte le solite trasformazioni fatto per i "parametri": R(Args...) ->R(*)(Args...) e soprattutto T[N] ->T* e di alto livello CV- qualificazioni scartando (§8.3.5/5):

struct X{ 
    bool operator()(int (&&arr)[3]); 
    long operator()(void*); 
}; 

static_assert(std::is_same<std::result_of<X(int[3])>::type, bool>(), "/cry"); 

Live example. uscita:

error: static assertion failed: /cry

Gli altri problemi sono i primo livello CV-qualificazioni scartando:

struct Y{}; 

struct X{ 
    bool operator()(Y const&); 
    long operator()(Y&&); 
}; 

Y const f(); 

static_assert(std::is_same<std::result_of<X(Y const)>::type, bool>(), "/cry"); 

Live example. uscita:

error: static assertion failed: /cry

+0

+1, questa è la risposta più completa fino ad ora –

+0

(ma per quanto mi riguarda, non credo che l'approccio "std :: tuple" faux varadic sarebbe stato troppo difficile, davvero ... potrei sbagliarmi comunque) –

+0

@Stephen: Dovresti provare ripetutamente se la fine dell'elenco degli argomenti corrente è '_Nil', e in caso contrario, aggiungi l'argomento successivo. Per questo, hai bisogno di nuovi parametri variadici. Come si fa in C++ 03 ... oh, giusto, tipo di funzione o argomenti predefiniti. : P – Xeo

4

result_of faceva parte di TR1, che è venuto fuori prima decltype è stato aggiunto al linguaggio. Ma è stato progettato pensando a decltype, quindi cambiare l'implementazione di result_of per usare decltype è semplice. Sì, è un hack, ma funziona.

+0

puoi elaborare? non hai bisogno comunque di variadics o faux variadics, per supportare un numero arbitrario di argomenti? non è che non ti creda, non è chiaro per me come faccia la differenza * a priori * –

+2

Sembra anche più ordinato. 'std :: result_of ' * assomiglia a * una chiamata di funzione 'S' con due argomenti di tipo' int'. –

+0

@KevinBallard si, ma non lo è! : D –

9

Penso che sia solo che qualcuno ha avuto l'idea che si potesse (ab) usare la notazione del tipo di funzione per imitare il modo in cui la rispettiva chiamata del funtore apparirebbe, e si è bloccata. Quindi, nessun motivo tecnico, solo uno estetico.

// the result type of a call to (an object of) type F, 
// passing (objects of) types A, B, and C as parameters. 
result_of<F(A, B, C)>::type 
+0

beh, sei sicuro che non ci siano ragioni tecniche?preferirei credere che ce ne siano alcuni che giustificano questo (ab) uso ... –

+2

+1 Questo è anche menzionato nel [rationale qui] (http://www.open-std.org/jtc1/sc22/wg21 /docs/papers/2003/n1454.html). –

2

(Ciò espande JohannesD's answer e Jesse Good's comment su di esso, ma questo non è adatta in un commento. Si prega di upvote che altro non rispondere a questa domanda)

Da N1454 Syntax and examples:.

The definition of the behavior of result_of is straightforward: given types F , T1 , T2 , ..., TN and lvalues f , t1 , t2 , ..., tN of those types, respectively, the type expression

result_of<F(T1, T2, ..., TN)>::type 

evaluates to the type of the expression f(t1, t2, ..., tN) .

Questo non abusa del sistema di tipi, è meravigliosamente elegante!

+0

Hah, è solo "splendidamente elegante" se si pensa che le similarità della sequenza di token arbitrari debbano essere consentite per determinare la semantica delle espressioni: D Inoltre, come per la risposta di @ Xeo, se capisco correttamente la definizione data non funziona sempre a causa del decadimento del tipo di funzione . –

Problemi correlati