2014-10-01 6 views
5

Widziałem konstrukt następującego typu w pewnym kodzie dzisiaj:Const char tablica o rozmiarze szablon argumentu vs. wskaźnik char

template<unsigned int N> unsigned int f(const char (&a)[N]); 

Czy istnieje jakiś rozsądny punkt posiadaniem przez ten posiada:

unsigned int f(const char *a); 

Niejasno rozumiem znaczenie podziału tego wskaźnika na wskaźnik, ale czy naprawdę jest tak źle, że musi zostać zastąpiony przez niezrozumiały kod dwukrotnie większy od rozmiaru?

(Niestety, nie mogę zapytać autora kodu, inaczej bym)

+0

Co oznacza "a" w drugim przykładzie? Pojedynczy 'int'? Prosta tablica? Ciąg C? Tablica zakończona znakiem NUL? Widzisz mój punkt? – Shoe

Odpowiedz

11

Intencją przekazując dowolną surowy wskaźnik do funkcji jest rozmówca ma jakiś pomysł:

  • Co wskazuje na.
  • Ile to wskazuje.

Łańcuchy w stylu C jako parametry wejściowe mają te ostatnie wywnioskowane, ponieważ zakłada się, że "ilu" jest uważane za przybycie do terminatora o wartości zerowej char.

Ale co, jeśli jesteś , a nie przekazujesz ciąg znaków w stylu C? Co jeśli jest to po prostu sekwencja zerowych lub więcej wartości char? Cóż, jeśli tak jest, to:

void f(const char *s) 
{ 
    // how many char does s refer to? 
} 

logiczna dedukcja byłoby to zrobić:

void f(const char *s, std::size_t N) 
{ 
    // Now the caller is telling us there are N chars at s 
} 

a to nie jest rzadkością w ogóle, choć potencjalnego punktu błąd, jeśli rozmówca przekazuje nam niewłaściwą długość (nigdy nie mów nigdy).

Co jednak, jeśli istnieje sposób, aby przesiać dane z rzeczywistej zmiennej typu przekazywane do funkcji za pomocą dedukcji za pomocą parametru szablonu nie typu? Co jeśli dzwoniący wywołuje nas ze stałą tablicą?

template<std::size_t N> 
void f(const char(&ar)[N]) 
{ 
    // we know the caller is passing a const-reference to a 
    // char array declared of size N. The value N can be used 
    // in this function. 
} 

Teraz znamy obie pozycje z naszej listy: "co" i "ile".Ponadto, możemy teraz zapewnić zarówno funkcję szablonu oraz przeciążenie i mają oba światy dostępne do nas:

// length specified implementation 
void f(const char *s, std::size_t N) 
{ 
    // caller passed N 
} 

// fixed buffer template wrapper 
template<std::size_t N> 
void f(const char(&ar)[N]) 
{ 
    f(ar,N); // invokes length-specified implementation from above. 
} 

I obie z poniższych będzie działać:

int main() 
{ 
    char buff[3]; 

    f(buff,3); 
    f(buff); 

} 

Więc jak jest ten dobry? Ponieważ następnego oznaczy błąd kompilatora, a nie realizacja dopasowywania można znaleźć:

int main() 
{ 
    char buff[3]; 
    const char *ptr = buff; 
    f(ptr); // ERROR: no matching function f(const char *) 
} 

Podsumowując jest to powszechną techniką, aby pomóc w zapewnieniu obie pozycje na naszej liście pocisku do wywoływany: „co” i "ile", bez konieczności posiadania długiej ręki sizeof(ar)/sizeof(*ar) za każdym razem, gdy jako parametr wejściowy używasz natywnej macierzy o stałej długości.

Powodzenia.

2

Wersja szablon pobiera tablicę o określonej wielkości. Wersja wskaźnika przyjmuje tylko wskaźnik, bez jakiejkolwiek koncepcji wielkości danych wskazywanych przez wskaźnik.

1

template w instancji dla każdej odrębnej wartości N (tak tam kod dodatkowy maszyna generowanej), ale wewnątrz f to nic nie kosztuje, aby wykorzystać swoją wiedzę N robić, co jest przydatne (np przetworzyć odpowiednią liczbę zgłoszeń z a odpowiednio size inna tablica znaków, której może potrzebować). Musisz zajrzeć do środka f, aby dowiedzieć się, czy faktycznie użyto N i czy jest to przydatne. W wersji bez szablonu wywołujący nie dostarcza rozmiaru tablicy/danych, więc nie ma dostarczonych równoważnych informacji (jeśli dane o różnych wartościach a są zmienne, to f musiałoby polegać na innym sposobie sprawdzenia, ile danych jest , np. strlen() poświęcenie czasu na wyszukanie kończącej NUL).

+0

Twój przykład strlenowy jest nieprawidłowy. W każdym razie powinniśmy wyszukać '\ 0', jeśli * rzeczywisty * rozmiar łańcucha jest mniejszy niż rozmiar tablicy> o < – ikh

+0

@ikh: to nie czyni mojego przykładu * błędnym * - to kwestia obsługiwanego użycia i szablonu Forma jest zwykle wykonywana * właśnie dlatego, że * wartość 'N' będzie w tym użyciu znacząca bez odwoływania się do' strlen' itd. (nawet jeśli okazyjnie tylko poprzez podanie użytecznej górnej granicy). –

3

Wskaźniki nie przechowują informacji, czy wskazują na pojedynczy obiekt lub pierwszy obiekt tablicy. Więc jeśli na przykład napisać w ciele funkcji

unsigned int f(const char *a); 

ekspresji

sizeof(a)/sizeof(*a) 

dostaniesz tylko wielkość samego wskaźnika. Jeśli użyjesz tego samego wyrażenia w ciele funkcji, otrzymasz rozmiar tablicy (oczywiście możesz użyć wartości N).

Więc gdy drugie podejście jest stosowane wtedy zazwyczaj zgłaszane są takie funkcje z dwoma parametrami, gdzie drugi parametr określa rozmiar tablicy

unsigned int f(const char *a, size_t n); 

Rozważmy sytuację, gdy trzeba dołączyć tablicę znaków przekazany jako Argument z innym ciągiem (załóżmy, że constator const jest nieobecny). Gdy użyjesz drugiej deklaracji, nawet funkcja strlen zastosowana do wskaźnika nie pomoże ci ustalić, czy oryginalny ciąg jest wystarczająco duży, aby mógł zostać dołączony. W pierwszym podejściu można użyć wyrażenia N - strlen(a), aby określić, czy w macierzy jest wystarczająca ilość miejsca.

3
template<unsigned int N> unsigned int f(const char (&a)[N]); 

Funkcja szablon trwa w tablicy jako odnośnik, co oznacza, że ​​gdy stosuje się bezpośrednio ze statycznie przeznaczonych C-tablic funkcji zna rozmiar tablicy podczas kompilacji.

unsigned int f(const char *a); 

Tutaj cała funkcja wie, że otrzymała wskaźnik do znaku stałego. Jeśli więc chciałbyś użyć go do manipulowania tablicą, musiałby przyjąć rozmiar tablicy jako dodatkowy argument, ponieważ nie ma sposobu na odzyskanie tej informacji z samego wskaźnika.

1

Istnieją dwa powody, które używają:

  • nie jest możliwe, aby przejść do tej funkcji szablonu bynajmniej tablic c stylu. Zatem wywołanie tej metody za pomocą wskaźnika byłoby błędem kompilacji.
  • w funkcji szablonu można uzyskać rozmiaru tablicy obliczane w czasie kompilacji

Więc tak:

const char* p="abc"; 
f(p); 

powoduje kompilację na niepowodzenie:

garbage.cpp:39:5: error: no matching function for call to ‘f(const char*&)’ 
    f(p); 
    ^
garbage.cpp:39:5: note: candidate is: 
garbage.cpp:21:39: note: template<unsigned int N> unsigned int f(const char (&)[N]) 
template<unsigned int N> unsigned int f(const char (&a)[N]) 

co następuje: , to:

f("abc"); 

jest w porządku, i masz rozmiar tej tablicy określonej jako kompilacja stałej