Eu trabalho em um aplicativo de software grande que deve ser executado em várias plataformas. Algumas dessas plataformas oferecem suporte a alguns recursos do C ++ 11 (por exemplo, MSVS 2010) e outras não (por exemplo, GCC 4.3.x). Espero que essa situação continue por vários anos (meu melhor palpite: 3-5 anos).
Dado isso, gostaria de configurar uma interface de compatibilidade para que (na medida do possível) as pessoas possam escrever código C ++ 11 que ainda será compilado com compiladores mais antigos com um mínimo de manutenção. No geral, o objetivo é minimizar o número de # ifdef o máximo possível e, ao mesmo tempo, habilitar a sintaxe / recursos básicos do C ++ 11 nas plataformas que os suportam e fornecer emulação nas plataformas que não o fazem.
Vamos começar com std :: move (). A maneira mais óbvia de obter compatibilidade seria colocar algo assim em um arquivo de cabeçalho comum:
#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)
Isso permite que as pessoas escrevam coisas como
std::vector<Thing> x = std::move(y);
... com impunidade. Ele faz o que eles querem no C ++ 11 e faz o melhor que pode no C ++ 03. Quando finalmente eliminamos o último dos compiladores C ++ 03, esse código pode permanecer como está.
No entanto, de acordo com o padrão, é ilegal injetar novos símbolos no std
espaço para nome. Essa é a teoria. Minha pergunta é: na prática, existe algum mal em fazer isso como uma maneira de obter compatibilidade futura?
Respostas:
Eu tenho trabalhado por um bom tempo em manter um nível de compatibilidade com versões anteriores e anteriores em meus programas C ++, até que finalmente tive que criar um kit de ferramentas de biblioteca , que
eu estou preparando para o lançamentojá foi lançado. Em geral, desde que você aceite que não será "perfeito" com compatibilidade direta nem nos recursos (algumas coisas simplesmente não podem ser emuladas para frente) nem na sintaxe (você provavelmente precisará usar macros, espaços de nomes alternativos para algumas coisas), então está tudo pronto.Há muitos recursos que podem ser emulados no C ++ 03 em um nível suficiente para uso prático - e sem todo o aborrecimento que vem com, por exemplo: Boost. Heck, mesmo a proposta de padrões C ++ para
nullptr
sugere um backport C ++ 03. E há o TR1, por exemplo, para tudo o que é C ++ 11, mas tivemos visualizações há anos. Além disso, alguns recursos do C ++ 14 , como variantes de asserção, functores transparentes eoptional
podem ser implementados no C ++ 03!As únicas duas coisas que sei que não podem ser absolutamente suportadas são modelos constexpr e variáveis.
Com relação a toda a questão de adicionar coisas ao namespace
std
, minha opinião é que não importa - de maneira alguma. Pense no Boost, uma das bibliotecas C ++ mais importantes e relevantes, e sua implementação do TR1: Boost.Tr1. Se você deseja melhorar o C ++, torne-o compatível com o C ++ 11 e, por definição, está transformando-o em algo que não é o C ++ 03; portanto, bloquear-se sobre um padrão que você pretende evitar ou deixar para trás é , simplesmente, contraproducente. Os puristas reclamam, mas, por definição, não é necessário se preocupar com eles.Claro, só porque você não seguirá o (03) Padrão, afinal, não significa que você não pode tentar ou que iria alegremente sair por aí quebrando-o. Essa não é a questão. Contanto que você mantenha um controle muito cuidadoso sobre o que é adicionado ao
std
espaço para nome e tenha um controle dos ambientes em que seu software é usado (por exemplo: faça testes!), Não deve haver nenhum dano intratável. Se possível, defina tudo em um espaço para nome separado e adicione apenasusing
diretivas ao espaço para nome,std
para que você não adicione nada além do que "absolutamente" precisa entrar. Qual, IINM, é mais ou menos o que o Boost.TR1 faz.Atualização (2013) : conforme a solicitação da pergunta original e vendo alguns dos comentários aos quais não posso adicionar devido à falta de repetição, aqui está uma lista dos recursos C ++ 11 e C ++ 14 e seu grau de portabilidade para C ++ 03:
nullptr
: totalmente implementável, considerando o backport oficial do Comitê; você provavelmente precisará fornecer também algumas especializações type_traits para que seja reconhecido como um tipo "nativo".forward_list
: totalmente implementável, embora o suporte ao alocador dependa do que a implmenentação do Tr1 pode oferecer.vector<int> v = {1, 2, 3, 4};
: totalmente implementáveis, embora mais elaboradas do que se gostaria.static_assert
: quase totalmente implementável quando implementado como uma macro (você só precisa ter cuidado com vírgulas).unique_ptr
: quase totalmente implementável, mas você também precisará de suporte para chamar o código (para armazená-lo em contêineres, etc); veja o abaixo embora.static_cast<>
pode ser quase impossível.noexcept
: depende dos recursos do seu compilador.auto
semântica edecltype
: depende de características do seu compilador - por exemplo .:__typeof__
.int16_t
, etc): depende dos recursos do seu compilador - ou você pode delegar no Portable stdint.h.::type
modelos para sempreconstexpr
: Não implementável ao meu conhecimento.dynarray
: totalmente implementável.optional<>
: quase totalmente implementável, desde que o seu compilador C ++ 03 suporte configurações de alinhamento.std::less<void>
para fazê-lo funcionar.assure
): totalmente implementável se você deseja assertivas, quase totalmente implementável se você deseja ativar arremessos.(Isenção de responsabilidade: vários desses recursos são implementados na minha biblioteca de backports C ++ que eu vinculei acima, então acho que sei do que estou falando quando digo "totalmente" ou "quase totalmente".)
fonte
Isso é fundamentalmente impossível. Considere
std::unique_ptr<Thing>
. Se fosse possível emular referências de rvalue como uma biblioteca, não seria um recurso de idioma.fonte
std::unique_ptr
-lo, mas há alguns outros recursos de referências de rvalue que não podem ser implementados no C ++ 03, portanto,std::forward
não é possível. A outra coisa é questd::unique_ptr
isso não será útil, porque as coleções não usarão a semântica de movimentação, a menos que você as substitua todas.unique_ptr
. Olhe para as falhas deauto_ptr
.unique_ptr
é praticamente o exemplo de livro didático de uma classe cuja semântica foi fundamentalmente ativada pelo recurso de idioma.unique_ptr
foi isso que foi fundamentalmente ativado pelo recurso de idioma. Não seria muito útil sem esse recurso. porque sem o encaminhamento perfeito, não seria utilizável em muitos casos, e o encaminhamento perfeito exige esse recurso.-std=c++0x
opção para habilitá-los).std
espaço para nome é um "comportamento indefinido". Isso significa que a especificação não diz o que vai acontecer. Mas se você souber que em uma plataforma específica a biblioteca padrão não define algo, basta seguir em frente e defini-lo. Apenas espere que você tenha que verificar em cada plataforma o que precisa e o que pode definir.unique_ptr
. No entanto, isso não seria muito útil, porque depende de coleções realmente usando a semântica de movimentação e as do C ++ 03 obviamente não.fonte