¿Qué son los comparadores transparentes?


En C++14, los contenedores asociativos parecen haber cambiado de C++11 – [asociativo.reqmts] / 13 dice:

Las plantillas de función miembrofind, count, lower_bound, upper_bound, y equal_range no participará en la resolución de sobrecarga a menos que exista el tipo Compare::is_transparent.

¿Cuál es el propósito de hacer que un comparador sea "transparente"?

C++14 también proporciona plantillas de biblioteca como esta:

template <class T = void> struct less {
    constexpr bool operator()(const T& x, const T& y) const;
    typedef T first_argument_type;
    typedef T second_argument_type;
    typedef bool result_type;
};

template <> struct less<void> {
    template <class T, class U> auto operator()(T&& t, U&& u) const
    -> decltype(std::forward<T>(t) < std::forward<U>(u));
    typedef *unspecified* is_transparent;
};

Así que, por ejemplo, std::set<T, std::less<T>> no tendría un transparente comparador, pero std::set<T, std::less<>> tendría uno.

¿Qué problema resuelve esto, y cambia esto cómo funcionan los contenedores estándar? Por ejemplo, los parámetros de plantilla de std::set siguen siendo Key, Compare = std::less<Key>, ..., por lo que el conjunto predeterminado pierde su find, count, etc. los miembros?

Author: ildjarn, 2013-12-02

4 answers

¿Qué problema resuelve esto,

Ver la respuesta de Dietmar y remyabel.

¿Y esto cambia cómo funcionan los contenedores estándar?

No, no por defecto.

La nueva plantilla de función miembro sobrecarga find etc. le permite usar un tipo que es comparable con la clave del contenedor, en lugar de usar el tipo de clave en sí. Ver N3465 por Joaquín Mª López Muñoz para la justificación y una propuesta detallada y cuidadosamente escrita para agregar esta característica.

En la reunión de Bristol, el LWG acordó que la función de búsqueda heteregenea era útil y deseable, pero no podíamos estar seguros de que la propuesta de Joaquín fuera segura en todos los casos. La propuesta N3465 habría causado serios problemas para algunos programas (ver la sección Impacto en el código existente). Joaquín preparó un borrador de propuesta actualizado con algunas implementaciones alternativas con diferentes compensaciones, lo que fue muy útil para ayudar al LWG a entender los pros y los contras, pero todos corrían el riesgo de romper algunos programas de alguna manera, por lo que no hubo consenso para agregar la característica. Decidimos que aunque no sería seguro agregar la función incondicionalmente, sería seguro si estuviera desactivada por defecto y solo "opt in".

La diferencia clave de la propuesta N3657 (que fue una revisión de último minuto por mí y STL basada en N3465 y un borrador no publicado posterior por Joaquín) fue agregar el tipo is_transparent como el protocolo que se puede utilizar para optar por la nueva funcionalidad.

Si no utiliza un "funtor transparente" (es decir, uno que define un tipo is_transparent), entonces los contenedores se comportan igual que siempre, y ese sigue siendo el valor predeterminado.

Si elige usar std::less<> (que es nuevo para C++14) u otro tipo de "funtor transparente", entonces obtendrá la nueva funcionalidad.

Usar std::less<> es fácil con plantillas de alias:

template<typename T, typename Cmp = std::less<>, typename Alloc = std::allocator<T>>
  using set = std::set<T, Cmp, Alloc>;

El nombre is_transparent viene de STL's N3421 que añadió los "operadores de diamante" a C++14. Un "funtor transparente" es aquel que acepta cualquier tipo de argumento (que no tiene que ser el mismo) y simplemente reenvía esos argumentos a otro operador. Tal funtor resulta ser exactamente lo que desea para la búsqueda heterogénea en contenedores asociativos, por lo que el tipo is_transparent se agregó a todos los operadores de diamante y se usó como el tipo de etiqueta para indicar que la nueva funcionalidad debe habilitarse en contenedores asociativos. Técnicamente, los contenedores no necesita un "funtor transparente", solo uno que admita llamarlo con tipos heterogéneos (por ejemplo, el tipo pointer_comp en https://stackoverflow.com/a/18940595/981959 no es transparente según la definición de STL, pero definir pointer_comp::is_transparent permite que se use para resolver el problema). Si sólo puedes buscar en tu std::set<T, C> con claves de tipo T o int entonces C sólo tiene que ser exigible con argumentos de tipo T y int (en cualquier orden), no necesitan ser verdaderamente transparente. Usamos ese nombre en parte porque no podíamos encontrar un nombre mejor (hubiera preferido is_polymorphic porque tales funtores usan polimorfismo estático, pero ya hay un rasgo de tipo std::is_polymorphic que se refiere al polimorfismo dinámico).

 43
Author: Jonathan Wakely,
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 11:46:55

En C++11 no hay plantillas de miembrosfind(), lower_bound(), etc. Es decir, no se pierde nada con este cambio. Las plantillas de miembros se introdujeron con n3657 para permitir el uso de claves heterogéneas con los contenedores asociativos. No veo ningún ejemplo concreto donde esto sea útil, excepto por el ejemplo que es bueno y malo!

El uso de is_transparent está destinado a evitar conversiones no deseadas. Si las plantillas miembro no estaban restringidas, el código existente puede pasar a través de objetos directamente que se habría convertido sin las plantillas de miembros. El ejemplo de caso de uso de n3657 es ubicar un objeto en un std::set<std::string> usando un literal de cadena: con la definición de C++11 se construye un objeto std::string cuando se pasan literales de cadena a la función miembro correspondiente. Con el cambio es posible usar el literal de cadena directamente. Si el objeto de la función de comparación subyacente se implementa exclusivamente en términos de std::string eso es malo porque ahora se crearía un std::string para cada comparación. Por otro lado, si el objeto de la función de comparación subyacente puede tomar un std::string y un literal de cadena, eso puede evitar la construcción de un objeto temporal.

El tipo anidado is_transparent en el objeto de función de comparación proporciona una forma de especificar si se debe usar la función miembro templada: si el objeto de función de comparación puede tratar con argumentos heterogéneos, define este tipo para indicar que puede tratar con diferentes argumentos de manera eficiente. Por ejemplo, la nueva función de operador los objetos simplemente delegan a operator<() y afirman ser transparentes. Eso, al menos, funciona para std::string que ha sobrecargado menos que los operadores que toman char const* como argumento. Dado que estos objetos de función también son nuevos, incluso si hacen lo incorrecto (es decir, requieren una conversión para algún tipo), al menos, no sería un cambio silencioso que resultaría en una degradación del rendimiento.

 28
Author: Dietmar Kühl,
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 22:34:24

Lo siguiente es todo copy-pasta de n3657.

P. ¿Cuál es el propósito de hacer que un comparador sea "transparente"?

A. Las funciones asociativas de búsqueda de contenedores (find, lower_bound, upper_bound, equal_range) solo toma un argumento de key_type, requiriendo usuarios para construir (ya sea implícita o explícitamente) un objeto de la key_type para hacer la búsqueda. Esto puede ser costoso, por ejemplo, la construcción de un objeto grande para buscar en un conjunto cuando el comparador solo funciona mira un campo del objeto. Hay un fuerte deseo entre los usuarios para poder buscar utilizando otros tipos que son comparables con el key_type.

P. ¿Qué problema resuelve esto

A. El LWG tenía preocupaciones sobre el código como el siguiente:

std::set<std::string> s = /* ... */;
s.find("key");

En C++11 esto construirá un único std:: string temporal y luego compáralo con elementos para encontrar la clave.

Con el cambio propuesto por N3465 la función std::set::find() ser una plantilla sin restricciones que pasaría el carácter const * a través de a la función de comparación, std:: less, que construya un std:: string temporal para cada comparación. El LWG consideró que este problema de rendimiento era un problema grave. El la función template find() también evitaría encontrar NULL en un contenedor de punteros, lo que hace que el código previamente válido ya no compilar, pero esto fue visto como un problema menos serio que el silencioso regresión del rendimiento

Q. esto cambia el funcionamiento de los contenedores estándar

A. Esta propuesta modifica los contenedores asociativos en y sobrecargando las funciones miembro de búsqueda con la función miembro plantilla. No hay cambios de idioma.

Q. también el conjunto predeterminado pierde su find, count, etc. miembros

A. Casi todo el código C++11 existente no se ve afectado porque el miembro las funciones no están presentes a menos que se utilicen nuevas características de la biblioteca C++14 como funciona la comparación.

Para citar Yakk ,

En C++14, std:: set:: find es una función de plantilla si Compare:: is_transparent exists. El tipo que pase no necesita sea clave, solo equivalente bajo su comparador.

Y n3657,

Añádase el párrafo 13 en 23.2.4 [asociativo.reqmts]: Las plantillas de función miembro find, lower_bound, upper_bound y equal_range no participar en la resolución de sobrecarga a menos que el tipo Comparar:: is_transparent no existe existe.

N3421 proporciona un ejemplo de "Funtores de operador transparente".

El código completo está aquí.

 17
Author: Community,
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 11:54:27

Stephan T Lavavej habla sobre los problemas en los que el compilador sigue creando temporarios, y cómo su propuesta de funtores operadores transparentes resolverá esto en c++1y

GoingNative 2013-Dont help the Compiler (alrededor de la hora)

 4
Author: woolstar,
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-04 18:52:11