2009-09-08 17 views
10

Czasami konieczne jest porównanie długości struny ze stałą.
Na przykład:Czy "strlen()" w czasie kompilacji działa efektywnie?

if (line.length() > 2) 
{ 
    // Do something... 
} 

Ale staram się unikać stałych „magiczne” w kodzie.
Zwykle używam takiego kodu:

if (line.length() > strlen("[]")) 
{ 
    // Do something... 
} 

Jest bardziej czytelne, ale nie efektywne ze względu na wywołaniu funkcji.
pisałem funkcje szablonów następująco:

template<size_t N> 
size_t _lenof(const char (&)[N]) 
{ 
    return N - 1; 
} 

template<size_t N> 
size_t _lenof(const wchar_t (&)[N]) 
{ 
    return N - 1; 
} 

// Using: 
if (line.length() > _lenof("[]")) 
{ 
    // Do something... 
} 

W kompilacji uwalniania (VisualStudio 2008) produkuje całkiem dobry kod:

cmp dword ptr [esp+27Ch],2 
jbe 011D7FA5 

I dobre jest to, że kompilator nie obejmuje "[]" łańcuch na wyjściu binarnym.

Czy jest to optymalizacja specyficzna dla kompilatora, czy też jest to typowe zachowanie?

+2

Prawdopodobnie możesz użyć jednego szablonu dla wszystkich typów tablic, coś w tym stylu: 'szablon size_t _lenof (const T (&) [N]) {return N - 1; } ', powinno nadal działać tak samo jak twój przykład. –

+2

@Evan Teran: dobry pomysł, ale te funkcje mają sens tylko dla ciągów znaków (tablica char/wchar_t) z powodu kończenia '\ 0'. Twoja funkcja będzie działać dla int [10] i powrotu 9 - nie sądzę, że to ma sens;) – Dmitriy

+0

@Dmitriy: rzeczywiście –

Odpowiedz

4

Zdolność do wywoływania wywołania funkcji jest zarówno specyficzną dla kompilatora optymalizacją, jak i zachowaniem. Oznacza to, że wiele kompilatorów może to zrobić, ale nie są do tego wymagane.

+0

Pożądana optymalizacja nie wymaga (just) wymaganego inliningu. Wymaga to obliczenia długości łańcucha podczas kompilacji. –

+0

To jednak nie jest optymalizacja. Długość nie zostanie obliczona w czasie wykonywania i nadal wywoła funkcję "_lenof". Czy standardowe * wymaganie * implementacji nie daje literałom ciągów typu 'const char [N]'? I nie są wartości tego typu wymagane, aby kompilator wywnioskować argumenty funkcji szablonu na 'N'? –

+0

Przykro mi, źle zrozumiałem, o czym mówi twoja odpowiedź - z jakiegoś powodu myślałem, że mówisz o "nieefektywnym z powodu wywołania funkcji [the strlen]". Jeśli kompilator nie może wstawić _lenof, to prawdopodobnie nie może niczego wstawić i byłby całkiem kiepskim kompilatorem C++. Każde poważne użycie szablonów byłoby koszmarne ... –

12

Dlaczego nie

 
sizeof "[]" - 1; 

(minus "[]" jeden za null wleczonego Można zrobić sizeof - sizeof '\ 0', ale sizeof '\ 0' często sizeof (int). w C, a "- 1" jest doskonale czytelny )

+0

nie działałoby dla szerokich ciągów znaków (na przykład L "[]"). – Dmitriy

+1

można naprawić dla szerokich ciągów znaków. Coś takiego jak: '(sizeof (L" [] ")/sizeof (L" ")) - 1' –

+0

@Evan Teran: yes, ale powinieneś użyć makr, aby uczynić go bardziej czytelnym. Makro IMHO jest bardziej w stylu C, ale nie w C++ – Dmitriy

-7
#define TWO 2 
#define STRING_LENGTH 2 
/* ... etc ... */ 

Poważnie, dlaczego przejść przez to wszystko, aby uniknąć kłopotów po prostu wpisując 2.? Szczerze myślę, że sprawiasz, że twój kod jest mniej czytelny, a inni programiści będą wpatrywać się w ciebie tak, jakbyś parsknął używaną kawą z filtra.

+0

to tylko przykład. W prawdziwym kodzie wygląda jak "jakiś ciąg". Czy w tym przypadku policzymy liczbę postaci? :) – Dmitriy

+0

Tak, jestem. I ja będę. I robię. –

+2

@Jed Smith: :) Czy jesteś pewien, że nie zapomnisz zmienić definicji makra, jeśli zmieni się ciąg? – Dmitriy

2

Myślę, że większość kompilatorów zoptymalizuje go na , gdy włączone są optymalizacje. Jeśli są wyłączone, może spowolnić twój program o wiele bardziej niż to konieczne.

Wolałbym twoje funkcje szablonu, ponieważ gwarantują, że nie zadzwonią strlen w czasie wykonywania. Oczywiście, zamiast pisać oddzielne funkcje char i wchar_t można dodać kolejny szablon argumentu, i dostać funkcję, która działa dla każdego rodzaju:

template <typename Char_t, int len> 
int static_strlen(const Char_t (&)[N] array){ 
    return len/sizeof(Char_t) - 1; 
} 

(jak już wspomniano w komentarzach, to daje zabawne rezultaty jeśli przekazano tablicę intów, ale czy to możliwe? Dotyczy to w szczególności napisów)

Ostatnia uwaga, nazwa _strlen to bad. Wszystkie nazwy w zakresie przestrzeni nazw zaczynające się od podkreślenia są zarezerwowane dla implementacji. Ryzykujesz jakieś paskudne konflikty nazewnictwa.

Nawiasem mówiąc, dlaczego "[]" jest mniej magicznej stałej niż 2?

W obu przypadkach jest to literał, który należy zmienić, jeśli format ciągu jest porównywany ze zmianami.

+0

Z jakiegoś powodu twoja funkcja nie wydaje się być szybsza niż użycie strlen. Wydaje się jednak, że jest szybszy niż użycie std :: char_traits :: length, więc nadal jest użyteczny, ponieważ strlen działa tylko na tablicach znaków. – leetNightshade

Powiązane problemy