È sbagliato utilizzare i modelli con l'ipotesi implicita che verranno definite alcune funzioni dei membri del tipo parametrizzato?

StackOverflow https://stackoverflow.com//questions/12714589

  •  13-12-2019
  •  | 
  •  

Domanda

Dì che scrivi una classe davvero cattiva

template <typename T>
class IntFoo
{
  T container ;
public:
  void add( int val )
  {
    // made an assumption that
    // T will have a method ".push_front".
    container.push_front( val ) ;
  }
} ;
.

Ignora il fatto che la classe presuppone che il contenitore sarà something<int>, invece presta attenzione al fatto che

IntFoo< list<int> > listfoo ;
listfoo.add( 500 ) ; // works

IntFoo< vector<int> > intfoo;
//intfoo.add( 500 ) ; // breaks, _but only if this method is called_..
.

In generale, è ok per chiamare una funzione membro di un tipo parametrizzato come questa ?Questo brutto design è un brutto design?Questo modello (anti) ha un nome?

È stato utile?

Soluzione

Questo è perfettamente bello e chiamato anatra di compilazione digitando e impiegati in tutti i tipi di luoghi in tutta la libreria standard stessa. E sul serio, come faresti qualsiasi cosa utile con un modello senza assumere l'argomento del modello di supportare determinate funzionalità?

Diamo un'occhiata a qualsiasi algoritmo nello stdlib, e.g., std::copy:

template<class InIt, class OutIt>
OutIt copy(InIt first, Init last, OutIt out){
  for(; first != last; ++first)
    *out++ = *first;
  return out;
}
.

Qui, viene assunto un oggetto di tipo InIt per supportare operator*() (per indirezione) e operator++() per aver avanzato l'iteratore. Per un oggetto di tipo OutIt, si presume che supporti operator*() Aswell e operator++(int). Un'assunzione generale è anche che ciò che viene restituito da *out++ è assegnabile (AKA convertibile) da qualsiasi rendimento di *first. Un'altra ipotesi sarebbe che sia InIt che OutIt sono copiati costruttibili.

Un altro luogo Questo è utilizzato è in qualsiasi contenitore standard. In C ++ 11, quando si utilizza std::vector<T>, T deve essere copiabile costruttibile se e solo se si utilizza qualsiasi funzione membro che richiede una copia.

Tutto ciò rende possibile che i tipi definiti dall'utente siano trattati come un tipo integrato, I.e., sono cittadini di classe fischi della lingua. Diamo un'occhiata ad alcuni algoritmi di nuovo, vale a dire quelli che assumono un callback che deve essere applicato su una gamma:

template<class InIt, class UnaryFunction>
InIt for_each(InIt first, InIt last, UnaryFunction f){
  for(; first != last; ++first)
    f(*first);
  return first;
}
.

InIt si presume che supporti nuovamente le stesse operazioni come nell'esempio copy sopra. Tuttavia, ora abbiamo anche UnaryFunction. Si presume che gli oggetti di questo tipo supportano la notazione delle chiamate della funzione post-fissaggio, in particolare con un argomento (UNARTO). Si presume inoltre che questo sia il parametro di questa chiamata di funzione sia convertibile da qualsiasi rendimento *first.

L'esempio tipico per l'utilizzo di questo algoritmo è con una funzione semplice:

void print(int i){ std::cout << i << " "; }

int main(){
  std::vector<int> v(5); // 5 ints
  for(unsigned i=0; i < v.size(); ++i)
    v[i] = i;
  std::for_each(v.begin(), v.end(), print); // prints '0 1 2 3 4 '
}
.

Tuttavia, è anche possibile utilizzare oggetti funzione per questo - un tipo definito dall'utente che sovraccarichi operator():

template<class T>
struct generate_from{
  generate_from(T first) : _acc(first) {}
  T _acc;
  void operator()(T& val){ val = _acc++; }
};

int main(){
  std::vector<int> v(5); // 5 ints
  // yes, there is std::iota. shush, you.
  std::for_each(v.begin(), v.end(), generate_from<int>(0)); // fills 'v' with [0..4]
  std::for_each(v.begin(), v.end(), print); // prints '0 1 2 3 4 '
}
.

Come puoi vedere, il mio tipo di tipo definito dall'utente può essere considerato esattamente come una funzione, può essere chiamato come se fosse una funzione . Si noti che faccio diverse ipotesi su generate_from in T, vale a dire che deve essere:

    .
  • copia-costruttibile (nel ciclo)
  • post-incrementale (nel generate_from)
  • copia-assegnabile (nel operator())
Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top