(Nota: Esta questão é sobre não precisar especificar o número de elementos e ainda permitir que tipos aninhados sejam inicializados diretamente.)
Esta questão discute os usos restantes para uma matriz C como int arr[20];
. Em sua resposta , @James Kanze mostra uma das últimas fortalezas das matrizes C, são características únicas de inicialização:
int arr[] = { 1, 3, 3, 7, 0, 4, 2, 0, 3, 1, 4, 1, 5, 9 };
Não precisamos especificar o número de elementos, viva! Agora, itere-o com as funções C ++ 11 std::begin
e std::end
com <iterator>
( ou suas próprias variantes ) e você nunca precisa nem pensar em seu tamanho.
Agora, existem formas (possivelmente TMP) de conseguir o mesmo std::array
? Uso de macros permitidas para torná-lo mais agradável. :)
??? std_array = { "here", "be", "elements" };
Edit : Versão intermediária, compilada a partir de várias respostas, tem a seguinte aparência:
#include <array>
#include <utility>
template<class T, class... Tail, class Elem = typename std::decay<T>::type>
std::array<Elem,1+sizeof...(Tail)> make_array(T&& head, Tail&&... values)
{
return { std::forward<T>(head), std::forward<Tail>(values)... };
}
// in code
auto std_array = make_array(1,2,3,4,5);
E emprega todo o tipo de coisas legais em C ++ 11:
- Modelos Variadic
sizeof...
- referências rvalue
- encaminhamento perfeito
std::array
, claro- inicialização uniforme
- omitindo o tipo de retorno com inicialização uniforme
- inferência de tipo (
auto
)
E um exemplo pode ser encontrado aqui .
No entanto , como @Johannes aponta no comentário da resposta do @ Xaade, você não pode inicializar tipos aninhados com essa função. Exemplo:
struct A{ int a; int b; };
// C syntax
A arr[] = { {1,2}, {3,4} };
// using std::array
??? std_array = { {1,2}, {3,4} };
Além disso, o número de inicializadores é limitado ao número de argumentos de função e modelo suportados pela implementação.
TMP
sua pergunta?Respostas:
O melhor que consigo pensar é:
No entanto, isso requer que o compilador faça NRVO e também pule a cópia do valor retornado (que também é legal, mas não obrigatório). Na prática, eu esperaria que qualquer compilador C ++ fosse capaz de otimizar isso de forma que seja tão rápido quanto a inicialização direta.
fonte
make_array
ligação.static_assert
um TMP para detectar quandoTail
não é implicitamente conversível eT
, em seguidaT(tail)...
, usando , mas que resta como um exercício para o leitor :)Eu esperaria um simples
make_array
.fonte
std::array<ret, sizeof...(T)>
nareturn
declaração. Isso inutilmente força a existência de um construtor de movimentação no tipo de matriz (em oposição a uma construção a partir deT&&
) no C ++ 14 e C ++ 11.Combinando algumas idéias de postagens anteriores, eis uma solução que funciona mesmo para construções aninhadas (testadas no GCC4.6):
Estranhamente, não é possível transformar o valor de retorno em uma referência de valor, que não funcionaria para construções aninhadas. Enfim, aqui está um teste:
(Para a última saída, estou usando minha impressora bonita .)
Na verdade, vamos melhorar o tipo de segurança dessa construção. Definitivamente, precisamos que todos os tipos sejam iguais. Uma maneira é adicionar uma asserção estática, que eu editei acima. A outra maneira é ativar apenas
make_array
quando os tipos são os mesmos, da seguinte forma:De qualquer forma, você precisará da
all_same<Args...>
característica do tipo variável . Aqui está, generalizar a partir destd::is_same<S, T>
(note que em decomposição é importante para permitir a mistura deT
,T&
,T const &
etc.):Observe que
make_array()
retorna por cópia de temporário, que o compilador (com sinalizadores de otimização suficientes!) Pode tratar como um rvalue ou, de outro modo, otimizar, estd::array
é um tipo agregado; portanto, o compilador pode escolher o melhor método de construção possível .Por fim, observe que você não pode evitar copiar / mover a construção ao
make_array
configurar o inicializador. Portantostd::array<Foo,2> x{Foo(1), Foo(2)};
, não possui cópia / movimentação, masauto x = make_array(Foo(1), Foo(2));
possui duas cópias / movimentação conforme os argumentos são encaminhadosmake_array
. Eu não acho que você pode melhorar isso, porque você não pode passar uma lista de inicializadores variados lexicamente para o auxiliar e deduzir o tipo e tamanho - se o pré-processador tiver umasizeof...
função para argumentos variados, talvez isso possa ser feito, mas não dentro do idioma principal.fonte
O uso da sintaxe de retorno à direita
make_array
pode ser ainda mais simplificadoInfelizmente para classes agregadas, requer especificação explícita de tipo
De fato, esta
make_array
implementação está listada no tamanho ... operadorversão c ++ 17
Graças à dedução de argumento de modelo para a proposta de modelos de classe , podemos usar guias de dedução para se livrar do
make_array
auxiliarCompilado com
-std=c++1z
sinalizador sob x86-64 gcc 7.0fonte
Sei que já faz algum tempo desde que essa pergunta foi feita, mas sinto que as respostas existentes ainda têm algumas falhas, então gostaria de propor minha versão ligeiramente modificada. A seguir estão os pontos que acho que faltam algumas respostas existentes.
1. Não há necessidade de confiar no RVO
Algumas respostas mencionam que precisamos confiar no RVO para retornar o construído
array
. Isso não é verdade; podemos usar a inicialização da lista de cópias para garantir que nunca haverá temporários criados. Então, em vez de:nós deveríamos fazer:
2. Faça
make_array
umaconstexpr
funçãoIsso nos permite criar matrizes constantes em tempo de compilação.
3. Não é necessário verificar se todos os argumentos são do mesmo tipo
Primeiro, se não estiverem, o compilador emitirá um aviso ou erro de qualquer maneira, porque a inicialização da lista não permite o estreitamento. Em segundo lugar, mesmo se realmente decidirmos fazer nossas próprias
static_assert
coisas (talvez para fornecer uma melhor mensagem de erro), ainda devemos provavelmente comparar os tipos decaídos dos argumentos em vez dos tipos brutos. Por exemplo,Se estamos simplesmente
static_assert
digitando issoa
,b
ec
temos o mesmo tipo, essa verificação falhará, mas provavelmente não é o que esperávamos. Em vez disso, devemos comparar seusstd::decay_t<T>
tipos (que são todosint
s)).4. Deduza o tipo de valor da matriz decaindo os argumentos encaminhados
Isso é semelhante ao ponto 3. Usando o mesmo trecho de código, mas não especifique o tipo de valor explicitamente dessa vez:
Provavelmente queremos fazer um
array<int, 3>
, mas as implementações nas respostas existentes provavelmente todas falham em fazer isso. O que podemos fazer é, em vez de retornar astd::array<T, …>
, retornar astd::array<std::decay_t<T>, …>
.Há uma desvantagem nessa abordagem: não podemos mais retornar um
array
tipo de valor qualificado para cv. Mas na maioria das vezes, em vez de algo parecido com umarray<const int, …>
, usamos um deconst array<int, …>
qualquer maneira. Existe uma troca, mas acho razoável. O C ++ 17std::make_optional
também adota essa abordagem:Levando em consideração os pontos acima, uma implementação completa do
make_array
C ++ 14 se parece com:Uso:
fonte
O C ++ 11 oferecerá suporte a esse tipo de inicialização para (a maioria?) Contêineres std.
fonte
std::vector<>
não precisa do número inteiro explícito e não sei porstd::array
que.{...}
sintaxe implica extensão constante em tempo de compilação; portanto, o ctor deve poder deduzir a extensão.std::initializer_list::size
não é umaconstexpr
função e, portanto, não pode ser usado assim. No entanto, há planos do libstdc ++ (a implementação fornecida com o GCC) para ter sua versãoconstexpr
.(Solução por @dyp)
Nota: requer C ++ 14 (
std::index_sequence
). Embora se possa implementarstd::index_sequence
em C ++ 11.fonte
make_array
como na resposta do Puppy.Implementação compacta do С ++ 17.
fonte
Se std :: array não for uma restrição e se você tiver o Boost, dê uma olhada
list_of()
. Isso não é exatamente como a inicialização de matriz do tipo C que você deseja. Mas perto.fonte
Crie um tipo de criador de matriz.
Sobrecarrega
operator,
para gerar um modelo de expressão encadeando cada elemento ao anterior por meio de referências.Adicione uma
finish
função livre que leve o criador da matriz e gere uma matriz diretamente da cadeia de referências.A sintaxe deve ser algo como isto:
Não permite a
{}
construção baseada, como somenteoperator=
. Se você estiver disposto a usar=
, podemos fazê-lo funcionar:ou
Nada disso parece uma boa solução.
O uso de variadics limita o limite imposto pelo compilador ao número de varargs e bloqueia o uso recursivo de
{}
subestruturas.No final, realmente não há uma boa solução.
O que faço é escrever meu código para que ele consuma ambos os dados
T[]
e agnosticamente - ele não se importa com o que eu os alimento. Às vezes, isso significa que meu código de encaminhamento precisa transformar cuidadosamente matrizes em s de forma transparente.std::array
[]
std::array
fonte