¿Puede el constructor de rango std :: vector invocar conversiones explícitas?

14

¿El siguiente programa está bien 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());
}

Según C ++ 17 [secuencia.reqmts], el requisito para

X u(i, j);

donde Xes un contenedor de secuencia, es:

Tserá EmplaceConstructibleen Xde *i.

Sin embargo, en el párrafo anterior se afirma que:

iy jdenotan iteradores que satisfacen los requisitos de iterador de entrada y hacen referencia a elementos implícitamente convertibles a value_type,

Por lo tanto, me parece que ambos requisitos tendrían que cumplirse: el tipo de valor del rango debe ser implícitamente convertible al tipo de valor del contenedor y EmplaceConstructible debe cumplirse (lo que significa que el asignador debe poder realizar la inicialización requerida) . Como intno es implícitamente convertible a A, este programa debe estar mal formado.

Sin embargo, sorprendentemente, parece compilarse bajo GCC .

Brian
fuente
(Para el registro, no es solo gcc: godbolt.org/z/ULeRDw )
Max Langhof
En este caso no se requiere conversión implícita, ya que el constructor explícito ya se ajusta al tipo. Creo que la descripción es confusa, pero la construcción explícita siempre es mejor que la conversión implícita antes de la construcción.
JHBonarius

Respuestas:

2

Es solo un requisito que los contenedores de secuencia admitan la construcción a partir de iteradores que satisfacen los criterios de convertibilidad implícita.

Esto por sí solo no permite que los contenedores de secuencias apoyen esa construcción desde iteradores que no satisfacen ese criterio hasta donde puedo decir 1 . Hay una regla explícita sobre eso:

Si se llama al constructor ... con un tipo InputIterator que no califica como iterador de entrada , entonces el constructor no participará en la resolución de sobrecarga.

No está claro qué significa "calificar como iterador de entrada" exactamente en el contexto. ¿Es una forma informal de expresar Cpp17InputIterator o intenta referirse a los requisitos de i y j? No lo sé. Ya sea que esté permitido o no, el estándar no tiene un requisito estricto para detectarlo:

[contenedor.requirements.general]

El comportamiento de ciertas funciones miembro de contenedor y guías de deducción depende de si los tipos califican como iteradores o asignadores de entrada. El grado en que una implementación determina que un tipo no puede ser un iterador de entrada no está especificado, excepto que, como mínimo, los tipos integrales no califican como iteradores de entrada. ...

Con la interpretación de que cualquier Cpp17InputIterator "califica como un iterador de entrada", no se requerirá que el programa de ejemplo esté mal formado. Pero tampoco se garantiza que esté bien formado.

1 En tal caso, podría considerarse un problema de calidad de implementación para advertir al confiar en él. Por otro lado, esta limitación a las conversiones implícitas puede considerarse un defecto .


PD: esto también se compila sin advertencias en Clang (con libc ++) y Msvc.

PPS Esta redacción parece haberse agregado en C ++ 11 (lo cual es natural, ya que también se introdujeron los constructores explícitos).

eerorika
fuente
1
Realmente depende de lo que significa "no califica como iterador de entrada" . A diferencia de lo anterior , en realidad no dice Cpp17InputIterator, por lo que no me queda claro si "y se refieren a elementos implícitamente convertibles a value_type" están incluidos en "iterador de entrada". Si es así, entonces el constructor no debería participar en la resolución de sobrecarga y el programa debería estar mal formado.
Max Langhof
1
Entonces, ¿cada clase de biblioteca estándar puede tener constructores adicionales sin emitir un diagnóstico cuando se usan esos constructores adicionales? Eso intuitivamente me parece mal ...
Brian
@Brian No estoy seguro de si son "constructores adicionales", pero tal vez más "implementaciones específicas de constructores que permitan más espacio". Verificar cada entrada puede tener un impacto significativo en el rendimiento, por lo que no sé si ese sería el camino a seguir ...
JHBonarius
@Brian Sin duda, sería una mala idea, incluso si no se rechaza explícitamente. En este caso, solo estamos considerando si un constructor requerido puede admitir tipos de iteradores que no es necesario admitir. En este caso, existe un requisito explícito de "no participará", como lo señaló Max, que ciertamente lo rechazaría. Pero no está claro qué significa "calificar como iterador de entrada" exactamente en el contexto. ¿Es una forma informal de expresarse Cpp17InputIteratoro intenta referirse a los requisitos de iy j? No lo sé.
eerorika
2
1) En C ++ establecemos el estándar bajo. . 2) Los constructores son funciones miembro no virtuales . 3) Ver LWG 3297 ; Sin embargo, no estoy particularmente convencido de que debamos eliminar el requisito de conversión implícita.
TC