2017-12-31 110 views
2

Angenommen, ich möchte eine universelle Funktion schreiben, die den Standardausgabebereich einer Sammlung ausgibt. Da es ich sein universal sollte davon ausgehen, dass ...Wie man den Typ des Template-Arguments bestätigt STL-Iterator Typ

std::vector<std::string> names = { "John", "Henry", "Mark" }; 

sowie:

std::vector<int> years = { 100, 200, 400 }; 

.. wird ausgedruckt möglich sein.

Da Arten von Sammlung verschieden sein können, und es gibt keine Basisklasse für STL Sammlung gibt mir Gelegenheit Basisklasse Iteratoren zu passieren verwende ich Template-Funktion:

template<typename TIterator> 
void PrintRange(TIterator beginIter,TIterator endIter) 
{   
    for(auto it = beginIter; it != endIter; ++it) 
    { 
     std::cout << *it << std::endl; 
    } 
} 

Alles ist jetzt gut funktioniert, jetzt kann ich schreiben :

PrintRange(names.begin(), names.end()); 

und:

PrintRange(years.begin(), years.end()); 

Aber jetzt er ich will lp Klient meiner Funktion, um schneller zu verstehen, warum es einen Fehler gibt, wenn er es benutzt. Nun, wenn ich rufe:

PrintRange(100, 400); 

Es Fehler ist:

main.cpp:23:34: error: invalid type argument of unary ‘*’ (have ‘int’)

Ich möchte so etwas wie drucken:

One of arguments does not correspond to expected argument of type 'iterator'

Also, was Ansatz für dieses Problem ist am besten:

  1. Es ist nicht wichtig, das zu kümmern die Fehlermeldung ist nicht so sinnvoll wie ich es erwartet hatte . Benutzer sollte Vorlagenklassencode zu Grund seines Fehlers analysieren ermitteln.

  2. Verwenden Sie static_assert, um alle bekannten Möglichkeiten zu bestätigen .. aber wie kann man behaupten, dass das Argument der Funktion ANY Iterator ist, da es keine Basisklasse gibt?

static_assert(std::is_base_of::iterator >::value);

Dieser Vektor von String Iterator nur behaupten würde ...

+0

Warum ist der Anfang und das Ende Iterator verschiedener Typen? Sie sollten von derselben sein – Fureeish

+0

Wenn Sie Ihrer Funktion eine gute Dokumentation hinzufügen, muss der Benutzer keinen Ihrer Vorlagencodes überprüfen und Sie müssen keine zusätzlichen Anstrengungen unternehmen, um Eingaben zu validieren. Lassen Sie den Compiler validieren! –

Antwort

2

Persönlich denke ich, dass Ihr erster Ansatz völlig in Ordnung ist, so dass Sie nicht viel über zusätzliche Fehlermeldung interessieren könnten.

Wenn Sie sich dagegen entscheiden, eine aussagekräftige Nachricht zu drucken, implementieren Sie möglicherweise ein benutzerdefiniertes Typmerkmal für die Erkennung von Iteratoren, wie es erklärt wird here und dann verwenden Sie es mit static_assert. So verwandelt sich der Code in so etwas wie:

template<typename TIterator> 
void PrintRange(TIterator beginIter, TIterator endIter) 
{   
    static_assert(is_iterator<TIterator>::value, 
     "TIterator is not an iterator type"); 

    for(auto it = beginIter; it != endIter; ++it) 
    { 
     std::cout << *it << std::endl; 
    } 
} 
0

Die Antwort zur Verfügung gestellt von Edgar Rokyan ist sehr hilfreich, aber ich bin mir dessen bewusst eine andere Lösung (wahrscheinlich ein schlechter ein, da wir viel mehr Code implementieren).

Diese Lösung ist nicht genau eine Typüberprüfung für einen Iterator, sondern eher ein Hinweis, in welche Richtung wir gehen können.In Anbetracht Ihrer PrintRange-Funktion gehen wir davon aus, dass wir 3 Operatoren für TIterator - , operator++ und operator != definieren müssen.

Um zu überprüfen, ob ein Operator definiert ist, können Sie diese verwenden können:

wird auf das Vorhandensein von operator* in T Implementierung überprüfen Dieser Code
template<typename T> 
struct has_deref_op{ 
    private: 
     template<typename U> 
     static constexpr auto test(int) -> decltype(std::declval<U>().operator*() == 1, 
                std::true_type()); 

     template<typename U> 
     static constexpr std::false_type test(...); 

    public: 
     static constexpr bool value = std::is_same<decltype(test<T>(0)), 
                std::true_type>::value; 
}; 

. Sie könnten dann fügen Sie ein static_assert, die es verwenden, um wird ein Argument zu überprüfen:

template<typename TIterator> 
void PrintRange(TIterator beginIter, TIterator endIter) 
{ 
    static_assert(has_deref_op<TIterator>::value, "argument must implement operator*"); 
    for(auto it = beginIter; it != endIter; ++it) 
    { 
     std::cout << *it << std::endl; 
    } 
} 

Diese Lösung hat einen großen Fehler - Es erfordert ziemlich viel Code schreiben, um eine Fehlermeldung zu vereinfachen. Um ehrlich zu sein, während dieser Ansatz ganz gut funktionieren würde, würde ich bei der Standardfehlermeldung bleiben. Es ist ziemlich selbsterklärend - wenn Sie eine int angeben, die operator* nicht definiert hat, erhalten Sie einen Fehler darüber.

EDIT: Nach dem Lesen der von Edgar in seiner Antwort verknüpften Frage, scheint es, dass es die Implementierung is_iterator empfiehlt, die ähnlich zu diesem Ansatz funktionieren würde. Mein schlecht für das Lesen nicht sorgfältig das erste Mal

Verwandte Themen