O construtor std :: vector range pode invocar conversões explícitas?

14

O programa a seguir está bem formado?

#include <vector>
struct A {
    explicit A(int) {}
};
int main() {
    std::vector<int> vi = {1, 2, 3, 4, 5};
    std::vector<A> va(vi.begin(), vi.end());
}

De acordo com o C ++ 17 [sequence.reqmts], o requisito para

X u(i, j);

onde Xé um contêiner de sequência, é:

Tserá EmplaceConstructiblepara Xpartir *i.

No entanto, no parágrafo anterior, afirma-se que:

ie jdenotam iteradores que atendem aos requisitos do iterador de entrada e se referem a elementos implicitamente conversíveis em value_type,

Assim, parece-me que ambos os requisitos precisariam ser atendidos: o tipo de valor do intervalo deve ser implicitamente conversível no tipo de valor do contêiner e EmplaceConstructible deve ser atendido (o que significa que o alocador deve ser capaz de executar a inicialização necessária) . Como intnão é implicitamente conversível em A, este programa deve ser mal formado.

No entanto, surpreendentemente, parece compilar sob o GCC .

Brian
fonte
(Para o registro, não só gcc: godbolt.org/z/ULeRDw )
Max Langhof
Nesse caso, nenhuma conversão implícita é necessária, pois o construtor explícito já se ajusta ao tipo. Eu acho que a descrição é confusa, mas a construção explícita é sempre melhor do que a conversão implícita antes da construção.
JHBonarius 31/10/19

Respostas:

2

É apenas um requisito para os contêineres de sequência apoiarem a construção de iteradores que atendem aos critérios de conversibilidade implícita.

Isso, por si só, não proíbe os contêineres de sequência de apoiar essa construção a partir de iteradores que não atendem a esse critério, até onde eu sei 1 . Há uma regra explícita sobre isso:

Se o construtor ... for chamado com um tipo InputIterator que não se qualifica como um iterador de entrada , o construtor não participará da resolução de sobrecarga.

Não está claro o que "qualificar como um iterador de entrada" significa exatamente no contexto. É uma maneira informal de expressar Cpp17InputIterator ou tenta se referir aos requisitos de iej? Eu não sei. Se é permitido ou não, o padrão não tem um requisito estrito para detectá-lo:

[container.requirements.general]

O comportamento de determinadas funções-membro do contêiner e guias de dedução depende se os tipos se qualificam como iteradores ou alocadores de entrada. A extensão em que uma implementação determina que um tipo não pode ser um iterador de entrada não é especificada, exceto que, no mínimo, os tipos integrais não se qualificam como iteradores de entrada. ...

Com a interpretação de que qualquer Cpp17InputIterator "se qualifica como um iterador de entrada", o programa de exemplo não precisa ser mal formado. Mas também não é garantido que seja bem formado.

1 Nesse caso, pode ser considerado um problema de qualidade de implementação a ser advertido ao confiar nele. Por outro lado, essa limitação às conversões implícitas pode ser considerada um defeito .


PS Isso compila sem avisos no Clang (com libc ++) e no Msvc.

PPS Esta expressão parece ter sido adicionada no C ++ 11 (o que é natural, como também foram introduzidos explicitamente construtores).

eerorika
fonte
11
Realmente depende do que "não se qualifica como um iterador de entrada" significa. Ao contrário do que foi dito acima , ele não diz Cpp17InputIterator, portanto, não está claro para mim se "e se referem a elementos implicitamente conversíveis em value_type" estão incluídos no "iterador de entrada". Se for, o construtor não deve participar da resolução de sobrecarga e o programa deve estar mal formado.
Max Langhof
11
Portanto, toda classe de biblioteca padrão pode ter construtores extras sem emitir um diagnóstico quando esses construtores extras são usados? Que intuitivamente parece errado para mim ...
Brian
@ Brian Não tenho certeza se seus "construtores extras", mas talvez mais "implementações específicas de construtores que permitam mais espaço". Verificando cada entrada pode ter impacto significativo no desempenho, então eu não sei se isso seria o caminho a percorrer ...
JHBonarius
@ Brian Seria certamente uma má idéia, mesmo que não explicitamente proibida. Nesse caso, estamos apenas considerando se um construtor necessário tem permissão para suportar tipos de iteradores que não precisam. Nesse caso, existe um requisito explícito de "não participe", conforme apontado por Max, o que certamente não permitiria isso. Mas, de fato, não está claro o que "qualificar como um iterador de entrada" significa exatamente no contexto. É uma maneira informal de expressar Cpp17InputIteratorou tenta se referir aos requisitos de ie j? Eu não sei.
eerorika
2
1) Em C ++, definimos o padrão baixo. . 2) Construtores são funções membro não virtuais . 3) Ver LWG 3297 ; no entanto, não estou particularmente convencido de que devemos remover o requisito implícito de conversão.
TC