(Histórico: Tenho alguma experiência na implementação de compiladores C e C ++.)
Matrizes de comprimento variável no C99 foram basicamente um passo em falso. Para dar suporte aos VLAs, o C99 teve que fazer as seguintes concessões ao bom senso:
sizeof x
nem sempre é uma constante em tempo de compilação; o compilador às vezes deve gerar código para avaliar uma sizeof
expressão em tempo de execução.
Permitindo VLAs bidimensionais ( int A[x][y]
) necessária uma nova sintaxe para declarar funções que levam 2D VLAs como parâmetros: void foo(int n, int A[][*])
.
Menos importante no mundo C ++, mas extremamente importante para o público-alvo de programadores de sistemas embarcados da C, declarar um VLA significa mastigar um pedaço arbitrariamente grande da sua pilha. Este é um estouro e empilhamento garantidos da pilha. (Sempre que você declara int A[n]
, está implicitamente afirmando que possui 2 GB de pilha de sobra. Afinal, se você sabe que " n
é definitivamente menor que 1000 aqui", então basta declarar int A[1000]
. Substituir o número inteiro de 32 bits n
por 1000
é uma admissão que você não tem idéia de qual deve ser o comportamento do seu programa.)
Ok, então vamos falar sobre C ++ agora. Em C ++, temos a mesma forte distinção entre "sistema de tipos" e "sistema de valores" que o C89 possui ... mas realmente começamos a confiar nele de maneiras que C não tem. Por exemplo:
template<typename T> struct S { ... };
int A[n];
S<decltype(A)> s; // equivalently, S<int[n]> s;
Se n
não fosse uma constante em tempo de compilação (ou seja, se A
fosse do tipo modificado de forma variável), qual seria o tipo de terra S
? O S
tipo também seria determinado apenas em tempo de execução?
Que tal isso:
template<typename T> bool myfunc(T& t1, T& t2) { ... };
int A1[n1], A2[n2];
myfunc(A1, A2);
O compilador deve gerar código para alguma instanciação de myfunc
. Como deve ser esse código? Como podemos gerar estaticamente esse código, se não sabemos o tipo de A1
em tempo de compilação?
Pior, e se, no tempo de execução n1 != n2
, isso acontecer !std::is_same<decltype(A1), decltype(A2)>()
? Nesse caso, a chamada para myfunc
nem deve ser compilada , porque a dedução do tipo de modelo deve falhar! Como poderíamos emular esse comportamento em tempo de execução?
Basicamente, o C ++ está se movendo na direção de empurrar cada vez mais decisões para o tempo de compilação : geração de código de modelo, constexpr
avaliação de funções e assim por diante. Enquanto isso, o C99 estava ocupado inserindo decisões tradicionalmente em tempo de compilação (por exemplo sizeof
) no tempo de execução . Com isso em mente, realmente faz sentido gastar algum esforço tentando integrar VLAs no estilo C99 no C ++?
Como todos os outros respondentes já apontaram, o C ++ fornece muitos mecanismos de alocação de heap ( std::unique_ptr<int[]> A = new int[n];
ou std::vector<int> A(n);
os mais óbvios) quando você realmente deseja transmitir a idéia "Não faço ideia de quanta RAM eu possa precisar". E o C ++ fornece um modelo bacana de tratamento de exceções para lidar com a situação inevitável de que a quantidade de RAM necessária é maior que a quantidade de RAM que você possui. Mas espero que esta resposta lhe dê uma boa idéia de por que os VLAs no estilo C99 não eram adequados para C ++ - e nem mesmo para C99. ;)
Para obter mais informações sobre o assunto, consulte N3810 "Alternativas para extensões de matriz" , artigo de outubro de 2013 de Bjarne Stroustrup sobre VLAs. O ponto de vista de Bjarne é muito diferente do meu; O N3810 se concentra mais em encontrar uma boa sintaxe C ++ ish para as coisas e em desencorajar o uso de matrizes brutas em C ++, enquanto eu me concentrei mais nas implicações para a metaprogramação e o sistema de tipos. Não sei se ele considera as implicações da metaprogramação / sistema de tipos resolvidas, solucionáveis ou meramente desinteressantes.
Uma boa publicação no blog que atinge muitos desses mesmos pontos é "Uso legítimo de matrizes de comprimento variável" (Chris Wellons, 27/10/2019).