¿Por qué 'make unique' no está permitido?


Asuma el espacio de nombres std a lo largo.

El borrador del comité C++14 N3690 define std::make_unique así:

[n3690: 20.9.1.4]: unique_ptr creación [único.ptr.crear]

template <class T, class... Args> unique_ptr<T> make_unique(Args&&... args);

1 Observaciones: Esta función no participará en la resolución de sobrecarga a menos que T no sea un array.
2 Devuelve: unique_ptr<T>(new T(std::forward<Args>(args)...)).

template <class T> unique_ptr<T> make_unique(size_t n);

3 Observaciones: Esta función no participará en la resolución de sobrecarga a menos que T sea una matriz de límite desconocido.
4 Devuelve: unique_ptr<T>(new typename remove_extent<T>::type[n]()).

template <class T, class... Args> unspecified make_unique(Args&&...) = delete;

5 Observaciones: Esta función no participará en la resolución de sobrecarga a menos que T sea una matriz de enlace conocido.

Ahora, esto me parece ser tan claro como el barro, y creo que necesita más exposición. Pero, dejando de lado este comentario editorial, creo que he descifrado los significados de cada variante:

  1. template <class T, class... Args> unique_ptr<T> make_unique(Args&&... args);

    Su bog-standard make_unique para tipos que no son de array. Presumiblemente, la "observación" indica que alguna forma de aserción estática o truco SFINAE es evitar que la plantilla se instancie con éxito cuando T es un tipo de matriz.

    En un nivel alto, véalo como el puntero inteligente equivalente a T* ptr = new T(args);.

  2. template <class T> unique_ptr<T> make_unique(size_t n);

    Una variante para tipos de matriz. Crea una matriz dinámicamente asignada de n × Ts, y devuelve envuelto en un unique_ptr<T[]>.

    En un nivel alto, véalo como el puntero inteligente equivalente a T* ptr = new T[n];.

  3. template <class T, class... Args> unspecified make_unique(Args&&...)

    No permitido. "no especificado" probablemente sería unique_ptr<T[N]>.

    De lo contrario sería el puntero inteligente equivalente a algo como el T[N]* ptr = new (keep_the_dimension_please) (the_dimension_is_constexpr) T[N]; no válido.

En primer lugar, ¿estoy en lo cierto? Y, si es así, ¿qué está pasando con la tercera función?

  • Si está ahí para rechazar a los programadores de intentar asignar dinámicamente una matriz mientras proporciona argumentos de constructor para cada elemento (al igual que new int[5](args) es imposible), entonces eso ya está cubierto por el hecho de que la primera función no puede ser instanciada para tipos de matriz, ¿no es así?

  • Si está ahí para evitar la adición al lenguaje de una construcción como T[N]* ptr = new T[N] (donde N es algo constexpr) entonces, bueno, ¿por qué? ¿No sería completamente posible que existiera un unique_ptr<T[N]> que envuelve un bloque dinámicamente asignado de N × Ts? ¿Sería esto algo tan malo, en la medida en que el comité ha hecho todo lo posible para rechazar su creación utilizando make_unique?

¿Por qué make_unique<T[N]> no está permitido?

Author: R. Martinho Fernandes, 2013-05-17

2 answers

Citando la propuesta original:

T[N]

A partir de N3485, unique_ptr no proporciona una especialización parcial para T[N]. Sin embargo, los usuarios estarán fuertemente tentados a escribir make_unique<T[N]>(). Este es un escenario sin victoria. Devolver unique_ptr<T[N]> seleccionaría el primario plantilla para objetos individuales, lo cual es extraño. Volviendo unique_ptr<T[]> sería una excepción a la regla de otra manera ironclad que make_unique<something>() devuelve unique_ptr<something>. Por lo tanto, este propuesta hace T[N] mal formado aquí, permitiendo que las implementaciones emitan static_assert mensajes útiles.

El autor de la propuesta, Stephan T. Lavavej, ilustra esta situación en este video sobre Core C++ (cortesía de chris), a partir del minuto 1:01:10 (más o menos).

 32
Author: Andy Prowl,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-05-23 12:16:56

Para mí, la tercera sobrecarga parece superflua, ya que no cambia el hecho de que las otras sobrecargas no coincidan con T[N] y no parece ayudar a generar mejores mensajes de error. Consider the following implementation:

template< typename T, typename... Args >
typename enable_if< !is_array< T >::value, unique_ptr< T > >::type
make_unique( Args&&... args )
{
  return unique_ptr< T >( new T( forward< Args >( args )... ) );
}

template< typename T >
typename enable_if< is_array< T >::value && extent< T >::value == 0, unique_ptr< T > >::type
make_unique( const size_t n )
{
  using U = typename remove_extent< T >::type;
  return unique_ptr< T >( new U[ n ]() );
}

Cuando intenta llamar a std::make_unique<int[1]>(1), el mensaje de error enumera ambos candidatos como desactivados por enable_if. Si agrega la tercera sobrecarga eliminada, el mensaje de error enumera tres candidatos en su lugar. Además, dado que se especifica como =delete;, no puede proporcionar una más mensaje de error significativo en el cuerpo de la tercera sobrecarga, por ejemplo, static_assert(sizeof(T)==0,"array of known bound not allowed for std::make_shared");.

Aquí está el ejemplo en vivo en caso de que quieras jugar con él.

El hecho de que la tercera sobrecarga terminó en N3656 y N3797 es probablemente debido a la historia de cómo make_unique se desarrolló con el tiempo, pero supongo que solo STL puede responder que:)

 0
Author: Daniel Frey,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2013-12-01 15:34:13