2015-10-06 8 views
11

Powiedzmy mam funkcji, takich jak:size() stanowi wskaźnik std :: tablicy w kontekście constexpr

int test(std::array<char, 8>* data) { 
    char buffer[data->size() * 2]; 

    [... some code ...] 
} 

wyraźnie wielkość bufora można ocenić w czasie kompilacji: Dane posiada constexpr rozmiar 8 elementy, 8 * 2 = 16 bajtów.

Jednak podczas kompilacji z -Wall, -pedantic i -std=c++11 uzyskać niesławny błąd:

warning: variable length arrays are a C99 feature [-Wvla-extension]

które wierzę sens: array::size() jest constexpr, ale nadal jest to metoda, w funkcji powyżej my nadal trzeba wyłuskać wskaźnik, który nie jest constexpr.

Gdy próbuję coś takiego:

int test(std::array<char, 8>& data) { 
    char buffer[data.size() * 2]; 
    [...] 
} 

gcc (wersja 5.2.0 próbowałem) wydaje się szczęśliwy: nie ma ostrzeżenia.

Ale z clang++ (3.5.1) nadal otrzymuję ostrzeżenie narzekające na tablice o zmiennej długości.

W moim przypadku nie mogę łatwo zmienić podpisu test, musi on przyjąć wskaźnik. Więc ... kilka pytań:

  1. Jaki jest najlepszy/najbardziej standardowy sposób, aby uzyskać rozmiar std::arraywskaźnik w constexpr kontekście?

  2. Czy różnica w zachowaniu jest oczekiwana w przypadku wskaźników i odniesień? Który kompilator ma rację co do ostrzeżenia, gcc lub clang?

+1

Byłoby interesujące dowiedzieć się dlaczego 'size' nie jest statyczna funkcja członek .. – dyp

+1

Powinienem też dodać, że nie ma chyba jakiś sposób, aby uzyskać rozmiar za pomocą szablonu, coś jak: ' szablon std :: size_t arraysize (const std :: array i array) {return N; } ' , którego można użyć powyżej. Nadal ... czy to właściwa droga? Wydaje się wykrzywiony. – rabexc

+0

Mogłem zobaczyć, że zawodzi w przypadku wskaźnika, ale nie w przypadku odniesienia, ponieważ technicznie, wskaźnik może być "nullptr"; z pewnością nie może w nim nic przydatnego, ale oznacza także, że "rozmiar" nie jest poprawnie zdefiniowany. Jeśli "rozmiar" byłby wirtualny, mógłbym zobaczyć, że narzeka on zarówno na wskaźniki, jak i referencje (ponieważ może to być typ pochodzący od 'std :: array ', który nie ma tej samej implementacji 'size', ale to wyraźnie nie jest sprawa tutaj. – ShadowRanger

Odpowiedz

2

nie wiem o 2.

Ale za 1, możemy to zrobić:

template<class T, size_t N> 
constexpr std::integral_constant<size_t, N> array_size(std::array<T, N> const&) { 
    return {}; 
} 

następnie:

void test(std::array<char, 8>* data) { 
    using size=decltype(array_size(*data)); 
    char buffer[size{}]; 
    (void)buffer; 
    // [... some code ...] 
} 

alternatywnie:

template<class T, class U, size_t N> 
std::array<T,N> same_sized_array(std::array< U, N > const&) { 
    return {}; 
} 

void test(std::array<char, 8>* data) { 
    auto buffer = same_sized_array<char>(*data); 
    (void)buffer; 
    // [... some code ...] 
} 

Wreszcie, C++ 14 Oczyszczanie:

template<class A> 
constexpr const decltype(array_size(std::declval<A>())) array_size_v = {}; 

void test3(std::array<char, 8>* data) { 
    char buffer[array_size_v<decltype(*data)>]; 
    (void)buffer; 
    // [... some code ...] 
} 

Live example.

+0

'constexpr const' wydaje mi się nieco zbędny .. – dyp

+0

@dyp Mogę dodać zmienne, jeśli chcesz?;) – Yakk

0

Stary dobry sposób C byłby zdefiniowany, ale C++ ma const int lub C++ 11 constexpr. Więc jeśli chcesz kompilator być świadomy, że rozmiar tablicy jest kompilacji stałą czasową, najbardziej przenośne (*) sposobem byłoby, aby to const lub constexpr:

#include <iostream> 
#include <array> 

const size_t sz = 8; // constexpr size_t sz for c++11 

int test(std::array<char, sz>* data) { 
    char buffer[sz * 2]; 

    buffer[0] = 0; 
    return 0; 
} 
int main() 
{ 
    std::array<char, sz> arr = { { 48,49,50,51,52,53,54,55 } }; 
    int cr = test(&arr); 
    std::cout << cr << std::endl; 
    return 0; 
} 

To kompiluje bez ostrzeżenia , nawet z -Wall -pedantic w Clang 3.4.1

Na drugie pytanie nie mogę sobie wyobrazić, dlaczego gcc robi różnicę między wskaźnikami a referencjami tutaj. Albo może stwierdzić, że metoda size() na stałej, która jest stałą, jest wyrażeniem stałym - i powinna zezwalać na obie - albo nie może - i powinna emitować to samo ostrzeżenie na obu. Dotyczy to nie tylko kompilatora, ale także standardowej implementacji biblioteki.

Prawdziwy problem polega na tym, że pre-C++ 11 std :: array nie należał do standardowej biblioteki, a constexpr jest również zdefiniowany tylko z C++ 11. Tak więc w trybie przed C++ 11 oba procesory kompilują std :: array jako rozszerzenie, ale metoda size nie może zadeklarować, że zwracana wartość jest stała. To wyjaśnia, dlaczego Clang (i gcc skierowane w stronę wskaźnika) emituje ostrzeżenie.

Ale jeśli kompilacji oryginalnego kodu w C++ 11 trybie (-std=c++11) nie powinno być ostrzeżenie, ponieważ standard wymaga size() sposób na std::array bycia constexpr.

(*) Pytanie dotyczy około best/most standard; Nie mogę powiedzieć, co jest najlepsze sposób, i nie mogę zdefiniować najbardziej standardowy albo, więc trzymam się tego, co chciałbym użyć, jeśli chciałem uniknąć problemów przenośności na kompilatorach nie C++ 11.

+0

* "Ale jeśli skompilujesz oryginalny kod w trybie C++ 11 (' -std = C++ 11') nie powinieneś mieć ostrzeżenia, ponieważ standard wymaga metody 'size()' na std :: array być 'constexpr'." * Funkcja 'constexpr' (member) nie musi wywoływać stałego wyrażenia po wywołaniu. – dyp

+0

@dyp: Dla mnie i dla [cppreference] (http://en.cppreference.com/w/cpp/language/constexpr) * [t] on 'constexpr' specifier deklaruje, że możliwe jest oszacowanie wartości funkcja lub zmienna w czasie kompilacji *. Tak więc, funkcja 'constexpr' bez parametrów * jest * wymagana, produkuje coś, co może być użyte wszędzie tam, gdzie mogłaby być stała świecenia. Jeśli przyjmuje parametry, musi to zrobić tylko wtedy, gdy jego parametry są wyrażeniami stałymi. –

+1

Oto przykład: 'struct foo {int i; constexpr int bar() {return i; }}; int main() {foo f {rand()}; constexpr auto x = f.bar(); } ' – dyp

0

Co powiesz na użycie numeru std::tuple_size na końcu danego parametru?

void test(std::array<char, 8>* data) { 
    using data_type = std::remove_pointer<decltype(data)>::type; 
    char buffer[std::tuple_size<data_type>::value * 2]; 
    static_assert(sizeof buffer == 16, "Ouch"); 
    // [... some code ...] 
} 
Powiązane problemy