En C ++ 14, los contenedores asociativos parecen haber cambiado de C ++ 11 - [associative.reqmts] / 13 dice:
Las plantillas de función miembro
find
,count
,lower_bound
,upper_bound
, yequal_range
no deberán participar en la resolución de sobrecarga menos que el tipoCompare::is_transparent
existe.
¿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í, por ejemplo, std::set<T, std::less<T>>
que no tienen un comparador transparente, pero std::set<T, std::less<>>
sería tener uno.
¿Qué problema resuelve esto y cambia el funcionamiento de los contenedores estándar? Por ejemplo, los parámetros de plantilla de std::set
siguen siendo Key, Compare = std::less<Key>, ...
, también lo hace el conjunto predeterminado perder sus find
, count
etc. miembros?
Respuestas:
Ver la respuesta de Dietmar y la respuesta de remyabel .
No, no por defecto.
Las nuevas sobrecargas de plantilla de función miembro de
find
etc. le permiten usar un tipo que es comparable con la clave del contenedor, en lugar de usar el tipo de clave en sí. Ver N3465 de 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 heterogénea 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 (consulte 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 cual fue muy útil para ayudar al LWG a comprender 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 función. Decidimos que, aunque no sería seguro agregar la función incondicionalmente, sería seguro si estuviera deshabilitada de forma predeterminada y solo "habilitada".
La diferencia clave de la propuesta N3657 (que fue una revisión de última hora por mí y STL basada en N3465 y un borrador inédito posterior de Joaquín) fue agregar el
is_transparent
tipo como el protocolo que se puede usar para optar por la nueva funcionalidad.Si no usa un "functor transparente" (es decir, uno que define un
is_transparent
tipo), los contenedores se comportan de la misma manera que siempre lo han hecho, y ese sigue siendo el predeterminado.Si elige usar
std::less<>
(que es nuevo para C ++ 14) u otro tipo de "functor transparente", obtendrá la nueva funcionalidad.Usar
std::less<>
es fácil con las plantillas de alias:El nombre
is_transparent
proviene del N3421 de STL que agregó los "operadores de diamante" a C ++ 14. Un "functor transparente" es uno que acepta cualquier tipo de argumento (que no tiene que ser el mismo) y simplemente reenvía esos argumentos a otro operador. Tal functor resulta ser exactamente lo que desea para la búsqueda heterogénea en contenedores asociativos, por lo que el tipois_transparent
se agregó a todos los operadores de diamante y se usó como tipo de etiqueta para indicar que la nueva funcionalidad debe habilitarse en contenedores asociativos. Técnicamente, los contenedores no necesitan un "functor transparente", solo uno que admita llamarlo con tipos heterogéneos (por ejemplo, elpointer_comp
tipo en https://stackoverflow.com/a/18940595/981959 no es transparente según la definición de STL,pointer_comp::is_transparent
permite que se utilice para solucionar el problema). Si sólo las operaciones de búsqueda nunca en sustd::set<T, C>
con claves de tipoT
oint
entoncesC
sólo tiene que haber exigible con argumentos de tipoT
yint
(en cualquier orden), que no tiene por qué ser verdaderamente transparente. Usamos ese nombre en parte porque no pudimos encontrar un nombre mejor (hubiera preferidois_polymorphic
porque tales functores usan polimorfismo estático, pero ya existe unstd::is_polymorphic
rasgo de tipo que se refiere al polimorfismo dinámico).fuente
<>
en la propuesta vinculada, pero esa propuesta no se introdujo<>
; es una sintaxis existente para una lista de parámetros de plantilla vacía. Los "operadores de diamante" serían un poco menos confusos.En C ++ 11 no son plantillas miembros
find()
,lower_bound()
, etc. Es decir, no se pierde nada por 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 en el que esto sea útil, excepto por el ejemplo que es bueno y malo!El
is_transparent
uso está destinado a evitar conversiones no deseadas. Si las plantillas de miembros no estuvieran restringidas, el código existente puede pasar directamente a través de objetos que se habrían convertido sin las plantillas de miembros. El caso de uso de ejemplo de n3657 es ubicar un objeto en unstd::set<std::string>
uso de un literal de cadena: con la definición de C ++ 11,std::string
se construye un objeto 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 destd::string
eso es malo porque ahorastd::string
se crearía un para cada comparación. Por otro lado, si el objeto de función de comparación subyacente puede tomar unstd::string
y un literal de cadena, que puede evitar la construcción de un objeto temporal.El
is_transparent
tipo anidado en el objeto de función de comparación proporciona una manera de especificar si se debe usar la función miembro con plantilla: 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, los nuevos objetos de función de operador simplemente deleganoperator<()
y afirman ser transparentes. Eso, al menos, funciona para lostd::string
que se ha sobrecargado menos que los operadores que tomanchar const*
como argumento. Dado que estos objetos de función también son nuevos, incluso si hacen algo incorrecto (es decir, requieren una conversión para algún tipo), al menos, no sería un cambio silencioso que resulte en una degradación del rendimiento.fuente
is_transparent
se define en el objeto de función de comparación de acuerdo con 23.2.4 [associative.reqmts] párrafo 13. Los objetos de función de comparación predeterminados son destd::less<Key>
acuerdo con 23.4.2 [associative.map.syn] y 23.4. 3 [asociativo.set.syn]. De acuerdo con 20.10.5 párrafo [comparación] 4 la plantilla general parastd::less<...>
no no definir un tipo anidadois_transparent
pero lastd::less<void>
especialización hace. Es decir, no, no obtiene un operador transparente por defecto.is_transparent
?Lo siguiente es todo copy-pasta de n3657 .
Para citar a Yakk ,
y n3657,
n3421 proporciona un ejemplo de "Funciones de operador transparentes" .
El código completo está aquí .
fuente
std::set<std::string>
Realmente se beneficia de "pasar elchar const *
paso", o necesita hacer unstd::set<std::string, std::less<>>
?With the change proposed by N3465 the std::set::find() function would be an unconstrained template which would pass the const char* through to the comparator function, std::less<std::string>, which would construct a std::string temporary for every comparison. The LWG considered this performance problem to be a serious issue.
Stephan T Lavavej habla sobre problemas en los que el compilador sigue creando temporales, y cómo su propuesta de functors de operador transparentes resolverá esto en c ++ 1y
GoingNative 2013: no ayude al compilador (aproximadamente a la hora)
fuente