2017-05-06 12 views
6

że mamy klasę, która może pisać rzeczy do wyjściaprojekt Interfejs: bezpieczeństwo funkcji przeciążonej biorąc ciąg i char tablicę

class Writer 
{ 
public: 
    int write(const std::string& str); 
    int write(const char* str, int len); 
    //... 
}; 

I było w porządku z tym, jej elastyczność i to wszystko, aż zdałem sobie sprawę

char* buf = new char[n]; //not terminated with '\0' 
//load up buf 
Writer w; 
w.write(buf); //compiles! 

To jest niepoprawny błąd naprawdę.

Możemy zmienić nieco z niektórych szablonów

class WriterV2 
{ 
public: 
    int write(const std::string& str); 
    int write(const char* str, int len); 
    template<typename... Args> 
    int write(const char*, Args...) 
    { static_assert(sizeof...(Args) < 0, "Incorrect arguments"); } 
    //... 
}; 

Ale ta metoda ma swoje problemy

WriterV2 w; 
w.write("The templating genius!"); //compile error 

Co mam zrobić? Czym jest lepszy projekt?

I zanim ktokolwiek zapyta, przeciążenie dla const char (&)[N]does not work. Może być możliwe utworzenie wrappera, aby to zrobić, ale wydaje się, że ... przesada?

EDYCJA Dodanie metody write(char*) i wysłanie błędu nie jest idealne. Po przejściu buf wokół funkcji i tak dalej, może to oznaczać const char*.

+1

Dlaczego po prostu nie zadeklarujesz metody obsługi tego przypadku, ale nie zastosuj go? int write (char * str); – ifma

+0

Okay, ale dlaczego musi istnieć rozróżnienie między tymi dwoma typami? Ponadto, wywołanie 'w.write (buf);' spowoduje _wejście_ wywołanie 'int Writer :: write (const std :: string & str);' Jeśli chcesz, aby wywołało 'int Writer :: write (const char * str, int len) ; ', a następnie dodaj parametr długości do twojego połączenia – InternetAussie

+0

Użyj std :: vector jako bufora –

Odpowiedz

5

ICS (Implicit Conversion Sequences) podczas rozpoznawania przeciążeniem w C++ może produkować zaskakujące rezultaty, jak zauważyłeś, a także dość irytujące ..

Można dostarczyć niezbędne interfejsy, których potrzebujesz, a następnie ostrożnie zatrudniać szablony do obsługi ciąg dosłowne vs const char* fiasko, korzystając z partial ordering do delete niechcianego przeciążenia.

KOD:

#include <iostream> 
#include <string> 
#include <type_traits> 

class Writer 
{ 
public: 
    int write(std::string&&) { std::cout << "int write(std::string)\n"; return 0; } 
    int write(const std::string&) { std::cout << "int write(const std::string& str)\n"; return 0; } 
    int write(const char*, int){ std::cout << "int write(const char* str, int len)\n"; return 0; } 

    template<std::size_t N = 0, typename = std::enable_if_t<(N > 0)> > 
    int write(const char (&)[N]) { std::cout << "int write(string-literal) " << N << " \n"; return 0; } 


    template<typename T> 
    int write(T&&) = delete; 

}; 

int main(){ 
    char* buf = new char[30]; 
    const char* cbuf = buf; 
    Writer w; 

    //w.write(buf);      //Fails! 
    //w.write(cbuf);     //Fails! 
    w.write(buf, 30);     //Ok! int write(const char*, int); 
    w.write(std::string("Haha"));  //Ok! int write(std::string&&); 
    w.write("This is cool");   //Ok! int write(const char (&)[13]); 
} 

reprodukcje:

int write(const char* str, int len) 
int write(std::string) 
int write(string-literal) 13 

Demo


Należy zauważyć, że rozwiązanie powyżej dziedziczy Wadą „przeciążenia funkcję ze swobodnych Forwarding odniesienia ". Oznacza to, że wszystkie ICS do typu argumentu funkcji wykonalnych w zestawie przeciążenia zostaną "usunięte".

+0

Dla kompletności, czy możesz dodać, że ma to wadę wyłączania * wszystkich * niejawnych konwersji na 'std :: string'? –

+0

@PasserBy, dodano. Dzięki! – WhiZTiM

Powiązane problemy