Pracuję na dużej aplikacji, która musi działać na kilku platformach. Niektóre z tych platform obsługują niektóre funkcje C ++ 11 (np. MSVS 2010), a niektóre nie obsługują żadnych (np. GCC 4.3.x). Oczekuję, że ta sytuacja utrzyma się przez kilka lat (moje najlepsze przypuszczenia: 3-5 lat).
Biorąc to pod uwagę, chciałbym skonfigurować interfejs kompatybilności w taki sposób, aby (w możliwym stopniu) ludzie mogli pisać kod C ++ 11, który nadal będzie się kompilował ze starszymi kompilatorami przy minimalnej konserwacji. Ogólnie rzecz biorąc, celem jest zminimalizowanie # ifdef w jak największym stopniu, przy jednoczesnym włączeniu podstawowej składni / funkcji C ++ 11 na platformach, które je obsługują, i zapewnienie emulacji na platformach, które nie obsługują.
Zacznijmy od std :: move (). Najbardziej oczywistym sposobem osiągnięcia zgodności byłoby umieszczenie czegoś takiego we wspólnym pliku nagłówkowym:
#if !defined(HAS_STD_MOVE)
namespace std { // C++11 emulation
template <typename T> inline T& move(T& v) { return v; }
template <typename T> inline const T& move(const T& v) { return v; }
}
#endif // !defined(HAS_STD_MOVE)
To pozwala ludziom pisać takie rzeczy
std::vector<Thing> x = std::move(y);
... bezkarnie. Robi, co chce w C ++ 11 i robi to, co może najlepiej w C ++ 03. Kiedy w końcu upuszczamy ostatni kompilator C ++ 03, ten kod może pozostać taki, jaki jest.
Jednak zgodnie ze standardem wprowadzanie nowych symboli do std
przestrzeni nazw jest nielegalne . To jest teoria. Moje pytanie brzmi: praktycznie mówiąc, czy jest coś złego w robieniu tego jako sposobu osiągnięcia kompatybilności z przyszłością?
Odpowiedzi:
Pracowałem od dłuższego czasu, utrzymując poziom kompatybilności do przodu i do tyłu w moich programach C ++, aż w końcu musiałem stworzyć z niego zestaw bibliotek , który
przygotowuję do wydania,został już wydany. Zasadniczo, o ile zaakceptujesz, że nie uzyskasz „doskonałej” zgodności do przodu ani w funkcjach (niektórych rzeczy po prostu nie można emulować w przód) w składni (prawdopodobnie będziesz musiał użyć makr, alternatywnych przestrzeni nazw dla niektóre rzeczy), to wszystko gotowe.Istnieje wiele funkcji, które można emulować w C ++ 03 na poziomie wystarczającym do praktycznego użycia - i bez wszystkich problemów związanych np. Z: Boost. Heck, nawet propozycja standardów C ++
nullptr
sugeruje backport C ++ 03. Jest też TR1 na przykład dla wszystkiego C ++ 11-ale-mieliśmy-mieliśmy-zapowiedzi-przez lata. Ponadto niektóre funkcje C ++ 14, takie jak warianty asertywne, przezroczyste funktory,optional
można zaimplementować w C ++ 03!Jedyne dwie rzeczy, o których wiem, że nie mogą być absolutnie backportowane, to constexpr i szablony variadic.
Jeśli chodzi o całą kwestię dodawania rzeczy do przestrzeni nazw
std
, uważam, że to nie ma znaczenia - wcale. Pomyśl o Boost, jednej z najważniejszych i najistotniejszych bibliotek C ++ oraz ich implementacji TR1: Boost.Tr1. Jeśli chcesz ulepszyć C ++, uczyń go zgodnym z C ++ 11, a następnie z definicji przekształcasz go w coś, co nie jest C ++ 03, więc blokowanie się ponad standardem, którego i tak chcesz uniknąć , najprościej mówiąc, przynosi efekt przeciwny do zamierzonego. Puryści będą narzekać, ale z definicji nie trzeba się nimi przejmować.Oczywiście, tylko dlatego, że nie będzie po (03) Standardowy przecież nie znaczy, że nie można próbować, czy pojedzie na wesoło obejść łamiąc go. Nie o to chodzi. Tak długo, jak zachowujesz bardzo ostrożną kontrolę nad tym, co jest dodawane do
std
przestrzeni nazw i masz kontrolę nad środowiskami, w których używane jest twoje oprogramowanie (tj.: Testuj!), Nie powinno być żadnej nieocenionej szkody. Jeśli to możliwe, zdefiniuj wszystko w osobnej przestrzeni nazw i dodajusing
do niej tylko dyrektywy, abystd
nie dodawać niczego poza tym, co „absolutnie” musi wejść. Co, IINM, jest mniej więcej tym, co robi Boost.TR1.Aktualizacja (2013) : jako żądanie pierwotnego pytania i widząc niektóre komentarze, których nie mogę dodać z powodu braku powtórzeń, oto lista funkcji C ++ 11 i C ++ 14 oraz ich stopień przenośności do C ++ 03:
nullptr
: w pełni możliwe do wdrożenia, biorąc pod uwagę oficjalne zaplecze Komitetu; prawdopodobnie będziesz musiał podać także niektóre specjalizacje type_traits, aby został rozpoznany jako typ „rodzimy”.forward_list
: w pełni możliwe do wdrożenia, chociaż wsparcie dla alokatora zależy od tego, co może zapewnić implementacja Tr1.vector<int> v = {1, 2, 3, 4};
: w pełni implementowalne, choć bardziej wyraziste niż byśmy tego chcieli.static_assert
: prawie w pełni możliwe do wdrożenia, gdy jest zaimplementowany jako makro (musisz tylko uważać na przecinki).unique_ptr
: prawie w pełni możliwe do wdrożenia, ale będziesz również potrzebować pomocy przy wywoływaniu kodu (do przechowywania ich w kontenerach itp.); patrz jednak poniżej.static_cast<>
mogą być prawie niemożliwe.noexcept
: zależy od funkcji kompilatora.auto
semantyka idecltype
: zależy od funkcji twojego kompilatora - np__typeof__
.:int16_t
itp.): zależą od funkcji kompilatora - lub możesz przekazać je na Portable stdint.h.::type
szablony na zawszeconstexpr
: Nie można wdrożyć o mojej wiedzy.dynarray
: w pełni implementowalny.optional<>
: prawie w pełni możliwy do wdrożenia, o ile kompilator C ++ 03 obsługuje ustawienia wyrównania.std::less<void>
aby działał.assure
): w pełni implementowalne, jeśli chcesz aserty, prawie w pełni implementowalne, jeśli zamiast tego chcesz włączyć rzuty.(Oświadczenie: kilka z tych funkcji jest zaimplementowanych w mojej bibliotece backportów C ++, którą podłączyłem powyżej, więc myślę, że wiem o czym mówię, kiedy mówię „w pełni” lub „prawie w pełni”).
źródło
Jest to zasadniczo niemożliwe. Zastanów się
std::unique_ptr<Thing>
. Gdyby można było emulować odwołania do wartości jako bibliotekę, nie byłby to funkcja językowa.źródło
std::unique_ptr
tam, ale niektóre inne cechy odwołań do wartości nie mogą być zaimplementowane w C ++ 03, więcstd::forward
nie jest to możliwe. Inną rzeczą jest to, żestd::unique_ptr
nie będzie to przydatne, ponieważ kolekcje nie będą używać semantyki przenoszenia, chyba że zastąpisz je wszystkie.unique_ptr
. Spójrz na wadyauto_ptr
.unique_ptr
jest praktycznie podręcznikowym przykładem klasy, której semantyka została zasadniczo włączona przez funkcję języka.unique_ptr
że funkcja języka została zasadniczo włączona. Bez tej funkcji nie byłby bardzo użyteczny. ponieważ bez idealnego przekazywania nie byłby on użyteczny w wielu przypadkach, a idealne przekazywanie wymaga tej funkcji.-std=c++0x
opcję, aby je włączyć).std
przestrzeni nazw jest „niezdefiniowanym zachowaniem”. Oznacza to, że specyfikacja nie mówi, co się stanie. Ale jeśli wiesz, że na konkretnej platformie biblioteka standardowa czegoś nie definiuje, po prostu idź i zdefiniuj to. Po prostu oczekuj, że będziesz musiał sprawdzić na każdej platformie, czego potrzebujesz i co możesz zdefiniować.unique_ptr
. Nie byłoby to jednak zbyt przydatne, ponieważ opiera się na kolekcjach wykorzystujących semantykę move, a C ++ 03 oczywiście nie.źródło