2009-02-28 11 views
12

Quali sono le cose più importanti che si sa sui modelli: caratteristiche nascoste, errori comuni, le pratiche migliori e più utili, consigli ... comuni errori/controllo/ipotesicose più importanti su modelli C++ ... lezione imparata

Sto iniziando a implementare la maggior parte della mia libreria/API utilizzando i modelli e vorrei raccogliere i modelli, i suggerimenti, ecc. Più comuni trovati nella pratica.

Lasciatemi formalizzare la domanda: qual è la cosa più importante che hai imparato sui modelli?

Prova a fornire esempi - sarebbe più facile da capire, al contrario di descrizioni contorte ed eccessivamente secco

Grazie

+0

Questa dovrebbe essere una wiki della comunità. È soggettivo. – strager

+0

Non penso che sia soggettivo. Da quello che ho letto, segue che Sasha vuole alcuni suggerimenti su come utilizzare i modelli. Tuttavia, la tua modifica potrebbe aver totalmente cambiato il significato della domanda originale. In tal caso, ignorare questo commento. – pyon

risposta

17

Da "Exceptional C++ stile", punto 7: risoluzione di sovraccarico funzione accade prima modelli di specializzazione. Non mescolare funzioni sovraccaricate e specializzazioni delle funzioni del modello, o si ha una brutta sorpresa a quale funzione viene effettivamente chiamata.

template<class T> void f(T t) { ... } // (a) 
template<class T> void f(T *t) { ... } // (b) 
template<> void f<int*>(int *t) { ... } // (c) 
... 
int *pi; f(pi); // (b) is called, not (c)! 

In cima a Articolo 7:

Peggio ancora, se si omette il tipo di modello di specializzazione, un diverso modello di funzione potrebbe ottenere specializzata a seconda dell'ordine di definizione e di conseguenza una funzione specializzata può o non può essere chiamata.

Caso 1:

template<class T> void f(T t) { ... } // (a) 
template<class T> void f(T *t) { ... } // (b) 
template<> void f(int *t) { ... }  // (c) - specializes (b) 
... 
int *pi; f(pi); // (c) is called 

Caso 2:

template<class T> void f(T t) { ... } // (a) 
template<> void f(int *t) { ... }  // (c) - specializes (a) 
template<class T> void f(T *t) { ... } // (b) 
... 
int *pi; f(pi); // (b) is called 
+0

Puoi dare un esempio di questo? –

+0

Sì. Anch'io sono stato preso a calci da questo. –

+0

Bel esempio! +1. :) –

0

E 'importante capire compilazione separata e la possibilità di conseguente aumento delle dimensioni eseguibili. Se istanziate il modello con lo stesso tipo in diversi file C++, otterrete il tipo riprodotto più volte, almeno su alcuni compilatori.

+0

Quali compilatori sarebbero? –

+0

Hmm, ho sicuramente creato un'istanza di un modello più che in una unità di traduzione, senza causare "scoppi", poiché l'istanziazione del modello non viola una regola di definizione. Tuttavia, se un compilatore ha un supporto scarso dei modelli, tutto è possibile, persino un WWIII! –

+0

I moderni linker scartano i duplicati. Questo è stato così per anni - non è davvero un problema di compilatore. –

4

Questa domanda è un po 'come "Sto implementando la maggior parte della mia libreria utilizzando le funzioni, quali sono gli errori più comuni nell'uso delle funzioni?" È difficile trovare risposte sensate a queste domande, ma ecco il mio consiglio: leggere un buon libro. Raccomando "C++ Templates" di Vandevoorde & Josuttis,

+0

Ho letto parti di questo libro - è buono. Tuttavia, voglio solo raccogliere suggerimenti comuni riscontrati da un programma. Grazie ... –

+2

@Neil: errori comuni nell'uso delle funzioni includono: passare i parametri per valore o per puntatore/riferimento quando si desidera il contrario; restituire puntatori/riferimenti a variabili locali o temporanee; basandosi sull'ordine di valutazione degli argomenti (che C++ non garantisce). –

2

Il STL è tuo amico.

+0

Non sempre, ma non so ancora perché il downvote. – pyon

2

Ecco alcune regole:

  1. non scrivere alcun modello a meno che non si sta scrivendo una libreria molto, molto generico (STL e Boost sono due esempi importanti).
  2. Non istanziare alcun modello non banale troppe volte. Istanziare enormi classi di template è particolarmente eccessivo. Dovresti considerare l'uso dell'ereditarietà e del polimorfismo (il modo semplice, intendo, usando le funzioni virtuali).
  3. Se si scrivono modelli, sapere quando utilizzare const, mutable e volatile salverà gli utenti del modello sia in fase di compilazione che di esecuzione.
  4. Se si sta creando un'istanza di modelli, utilizzare un buon compilatore.
5

avrei dovuto dire di Coplien Curiously Recurring Template Pattern (CRTP) è il trucco un modello che mi ritrovo raggiungendo per oltre & più volte. In sostanza, consente di iniettare funzionalità staticamente personalizzate in una classe derivata ereditando da una classe base parametrizzata sul nome della classe derivata. Incredibile, ma incredibilmente utile (alcuni lo chiamano polimorfismo statico).

Inoltre, ordinerò il consiglio di Neil Butterworth di leggere "Modelli C++" e inserire il numero Modern C++ Design di Alexandrescu.

2

Leggere i libri efficaci di AWL e C++ di Meyers e il Design moderno di C++ di Alexandrescu.

Meyers vi darà le basi su facili errori da fare e su come evitarli. Alexandrescu ti presenta un modello di programmazione basato su modelli che dovrebbe farti chiedere "È questa davvero una buona idea?" l'intero libro.

+0

Ho letto entrambi questi libri ... raccogliere suggerimenti da persone –

2

Io tendo ad utilizzare i modelli un bel po 'per evitare la duplicazione di codice, e per aumentare la sicurezza attraverso controlli di compilazione.

In generale, è utile pensare mentre si digita ciò che il compilatore sta per fare e come verrà generato il codice per ogni tipo.

Essendo molto iterativo nello sviluppo e nella costruzione della complessità del modello, a poco a poco mi ha aiutato a evitare di affondare nei messaggi di errore di compilazione. Non dimenticare di tenere una semplice (o finta) istanza del modello da qualche parte, altrimenti potresti avere delle brutte sorprese quando installi un modello di mostri per la prima volta.

Infine, quando non c'è via d'uscita, leggere questi messaggi di errore compilati! All'inizio potrebbero sembrare piuttosto spaventosi, ma sono davvero d'aiuto. Forse a prima cosa estraendo il primo, copiandolo in un editor di testo e facendolo sembrare carino, ci si abituerà a loro, e diventerà presto una seconda natura da leggere.

+1

Per evitare la ripetizione del codice SOURCE, intendi?Perché con i modelli hai un sacco di codice MACCHINA ripetuto. – pyon

7

Questo potrebbe non essere popolare, ma penso che sia necessario dirlo.

I modelli sono complicati.

Sono incredibilmente potenti, ma usali saggiamente. Non impazzire, non avere troppi argomenti del template ... Non hai troppe specializzazioni ... Ricorda, anche gli altri programmatori devono leggerlo.

E soprattutto, stare lontano da template metaprogrammazione ...

+0

Ma la metaprogrammazione del modello doveva essere la parte divertente di tutto questo! – pyon

+0

"E soprattutto, stai lontano dal modello metaprogramming ..." Punto ottenuto :-) – kyku

3

Template Consiglio del giorno: Lo sapevate che è possibile specializzarsi funzioni scelte di istanze di modello:

#include <iostream> 
#include <vector> 

namespace std { 
    template<> 
    void vector<int>::clear() { 
    std::cout << "Clearing..." << std::endl; 
    resize(0); 
    } 
} 

int main() { 
    std::vector<int> v; 
    v.push_back(1); 
    v.clear(); 
} 

ouputs: Compensazione ...

+0

Sapevo che ... uno buono –

4

Un errore comune è che un costruttore di modelli o un operatore di assegnazione non sopprimerà quello generato dal compilatore:

template <typename T> 
class A { 
public: 
    template <typename S> 
    A(A<S> const &); 

    template <typename S> 
    A & operator=(A<S> const &); 

private: 
    int * i; 
}; 

Sebbene queste funzioni assomiglino al gestore della copia e all'operatore di assegnazione copia, il compilatore non la vede in quel modo e genera comunque le versioni implicite.Il risultato è che ogni operazione eseguita con queste funzioni (es copia completa di un utente.) Non hanno luogo quando l'oggetto viene copiato o ceduta dallo stesso tipo:

void foo (A<int>); 

void bar() { 
    A<int> a1; 
    foo (a1); // Implicitly generated copy ctor called 

    A<long> a2; 
    foo (a2); // Template ctor called. 

    A<int> a3; 
    a3 = a1; // Implicitly generated copy assignment operator called 

    a3 = a2; // Template assignment operator called 
} 

La ragione di questo comportamento è dovuto una norma speciale nella risoluzione di sovraccarico (13.3.3):

date queste definizioni, una valida funzione F1 è definita come una migliore funzione di un'altra funzione vitale F2 se per tutti gli argomenti i, ICSI (F1) è non una sequenza di conversione peggiore di I CSi (F2), e poi

[...]

- F1 è una funzione non template e F2 è un modello di funzione di specializzazione, o, se non che,

Negli esempi precedenti, la risoluzione di sovraccarico vede due funzioni con la stessa firma, una delle quali è un modello. La funzione non template (l'operatore di copia/assegnazione copia copiato implicitamente generato) vince e così viene chiamata.

+0

Un altro modo per vederlo è che copy ctor e operator = sono modelli all'interno dei template. Quando viene creata un'istanza di A , i modelli di copia ctor e operator = membro non esistono ancora come funzioni reali, quindi vengono generati copy coptor implicito e operatore = per A . –

+1

Per evitare ciò per l'operatore di assegnazione, è possibile avere un membro non modello che delega a un membro del modello: A & operator = (const A & a) {operatore di ritorno = (a); } Non riesco a vedere alcun modo per fare questo per copy ctor, tranne che per scrivere un membro non-template separato. –

0

Ho usato molto C++ e modelli, incluso il metaprogramma più avanzato del modello e la mia sensazione è che la loro utilità sia sopravvalutata. Sono stati originariamente aggiunti al linguaggio C++, ben dopo la creazione di C++, per affrontare una capacità di programmazione generica. Ciò consente semplicemente di concentrarsi sulla logica del codice senza riguardo per i tipi, rendendo potenzialmente più chiaro e riutilizzabile il codice.

La mia filosofia di programmazione è comprendere lo scopo e il design originali della lingua e le sue caratteristiche per apprezzare davvero la lingua. Ritengo che la metaprogrammazione del modello sia la bastardizzazione dei modelli e dovrebbe essere evitata. I modelli sono comunque utili per definire tipi generici di livello superiore come il caso di Tuples.

Problemi correlati