tipos texto logicos hacer ejemplos construcción construccion como argumentos argumento argumentativo argumentacion argumenta c++ c++11 map std-pair emplace

c++ - texto - ejemplos de argumentos



¿Por qué necesito usar piecewise_construct en map:: emplace para constructores de argumentos únicos de objetos no copiables? (1)

Por lo que puedo decir, el problema no se debe a map::emplace , sino a los constructores de pair :

#include <map> struct A { A(int) {} A(A&&) = delete; A(A const&) = delete; }; int main() { std::pair<int, A> x(1, 4); // error }

Este ejemplo de código no se compila, ni con g ++ 4.8.1 de coliru ni con clang ++ 3.5, que están usando libstdc ++, por lo que puedo decir.

El problema está arraigado en el hecho de que aunque podemos construir

A t(4);

es decir, std::is_constructible<A, int>::value == true , no podemos convertir implícitamente un int en un A [conv] / 3

Una expresión e puede convertirse implícitamente a un tipo T si y solo si la declaración T t=e; Está bien formado, por alguna variable temporal inventada t .

Tenga en cuenta la copia de inicialización (el = ). Esto crea una A temporal e inicializa t desde este temporal, [dcl.init] / 17. Esta inicialización desde un intento temporal llama al ctor de movimiento eliminado de A , lo que hace que la conversión no esté bien formada.

Como no podemos convertir de un int a un A , el constructor de pair que uno esperaría que se llame será rechazado por SFINAE . Este comportamiento es sorprendente, N4387: mejora los análisis de pares y tuplas e intenta mejorar la situación, haciendo que el constructor sea explicit lugar de rechazarlo. N4387 ha sido votado en C ++ 1z en la reunión de Lenexa.

A continuación se describen las reglas de C ++ 11.

El constructor que esperaba que me llamaran se describe en [pairs.pair] / 7-9

template<class U, class V> constexpr pair(U&& x, V&& y);

7 Requiere: is_constructible<first_type, U&&>::value is true y is_constructible<second_type, V&&>::value es true .

8 Efectos: el constructor inicializa primero con std::forward<U>(x) y segundo con std::forward<V>(y) .

9 Observaciones: Si U no es convertible implícitamente a first_type o V no es implícitamente convertible a second_type este constructor no participará en la resolución de sobrecarga.

Note la diferencia entre is_constructible en la sección de is_constructible y "no es convertible implícitamente" en la sección de comentarios . Se cumplen los requisitos para llamar a este constructor, pero no puede participar en la resolución de sobrecarga (= tiene que ser rechazado a través de SFINAE).

Por lo tanto, la resolución de sobrecarga debe seleccionar una "coincidencia peor", es decir, una cuyo segundo parámetro es una A const& . Se crea un temporal a partir del argumento int y se vincula a esta referencia, y la referencia se utiliza para inicializar el miembro de datos de pair ( .second ). La inicialización intenta llamar a la copia eliminada ctor de A , y la construcción del par está mal formada.

libstdc ++ tiene (como extensión) algunos controladores no estándar. En el último doxygen (y en 4.8.2), el constructor del pair que esperaba llamar (sorprendiéndome por las reglas requeridas por el Estándar) es:

template<class _U1, class _U2, class = typename enable_if<__and_<is_convertible<_U1, _T1>, is_convertible<_U2, _T2> >::value >::type> constexpr pair(_U1&& __x, _U2&& __y) : first(std::forward<_U1>(__x)), second(std::forward<_U2>(__y)) { }

y el que realmente se llama es el no estándar:

// DR 811. template<class _U1, class = typename enable_if<is_convertible<_U1, _T1>::value>::type> constexpr pair(_U1&& __x, const _T2& __y) : first(std::forward<_U1>(__x)), second(__y) { }

El programa está mal formado de acuerdo con el Estándar, no es simplemente rechazado por este ctor no estándar.

Como observación final, aquí está la especificación de is_constructible y is_convertible .

is_constructible [meta.rel] / 4

Dada la siguiente función del prototipo:

template <class T> typename add_rvalue_reference<T>::type create();

la condición de predicado para una especialización de plantilla is_constructible<T, Args...> cumplirá si y solo si la siguiente definición de variable estaría bien formada para alguna variable inventada t :

T t(create<Args>()...);

[ Nota: estos tokens nunca se interpretan como una declaración de función. - nota final ] La verificación de acceso se realiza como en un contexto no relacionado con T y cualquiera de los Args . Solo se considera la validez del contexto inmediato de la inicialización de la variable.

is_convertible [meta.unary.prop] / 6:

Dada la siguiente función del prototipo:

template <class T> typename add_rvalue_reference<T>::type create();

la condición de predicado para una especialización de plantilla is_convertible<From, To> cumplirá si y solo si la expresión de retorno en el siguiente código estaría bien formada, incluidas las conversiones implícitas al tipo de retorno de la función:

To test() { return create<From>(); }

[ Nota: este requisito proporciona resultados bien definidos para tipos de referencia, tipos de nulos, tipos de matrices y tipos de funciones. - nota final ] La verificación de acceso se realiza como en un contexto no relacionado con To y From . Solo se considera la validez del contexto inmediato de la expresión de la declaración de retorno (incluidas las conversiones al tipo de retorno).

Para tu tipo A ,

A t(create<int>());

está bien formado; sin embargo

A test() { return create<int>(); }

crea un temporal de tipo A e intenta moverlo al valor de retorno (inicialización de la copia). Eso selecciona el ctor A(A&&) eliminado y, por lo tanto, está mal formado.

El siguiente código no se compilará en gcc 4.8.2. El problema es que este código intentará copiar la construcción de un std::pair<int, A> que no puede suceder debido a la struct A falta de copia y mover constructores.

¿Está fallando gcc aquí o me estoy perdiendo algo?

#include <map> struct A { int bla; A(int blub):bla(blub){} A(A&&) = delete; A(const A&) = delete; A& operator=(A&&) = delete; A& operator=(const A&) = delete; }; int main() { std::map<int, A> map; map.emplace(1, 2); // doesn''t work map.emplace(std::piecewise_construct, std::forward_as_tuple(1), std::forward_as_tuple(2) ); // works like a charm return 0; }