c++ templates stl typedef traits

c++ - (Re) named std:: pair miembros



templates stl (9)

En lugar de escribir town->first me gustaría escribir town->name . Los accessors nombrados en línea ( Renombrar primero y segundo a un iterador de mapa y los miembros con nombre std :: pair ) son las mejores soluciones que he encontrado hasta ahora. Mi problema con los accesores con nombre es la pérdida de seguridad de tipo: pair<int,double> puede referirse a struct { int index; double value; } struct { int index; double value; } struct { int index; double value; } o para struct { int population; double avg_temp; } struct { int population; double avg_temp; } struct { int population; double avg_temp; } . ¿Alguien puede proponer un enfoque simple, tal vez algo similar a los rasgos?

A menudo quiero devolver un par o una tupla de una función y es bastante agotador introducir un nuevo tipo como struct city { string name; int zipcode; } struct city { string name; int zipcode; } struct city { string name; int zipcode; } y su ctor cada vez. Estoy encantado de aprender sobre boost y C ++ 0x, pero necesito una solución pura de C ++ 03 sin boost.

Actualizar

Pregunta de Re andrewdski: sí, una sintaxis (hipotética) como pair<int=index, double=value> que crearía un tipo distinto del pair<int=population, double=avg_temp> cumpliría con sus requisitos. Ni siquiera me importa tener que implementar una vez una clase de plantilla de par / tupla personalizada y solo pasarle un argumento de plantilla de "rasgos de nombre" cuando necesito un nuevo tipo. No tengo idea de cómo se verían esos "rasgos de nombre". Tal vez sea imposible.


Aunque no es perfecto, es posible usar datos etiquetados:

template <typename tag_type, typename pair_type> typename tag_type::type& get(pair_type& p); typedef std::pair<std::string /*name*/, int /*zipcode*/> city; struct name { typedef std::string type; }; struct zipcode { typedef int type; }; template <> std::string& get<name, city>(city& city) { return city.first; } template <> int& get<zipcode, city>(city& city) { return city.second; } int main() { city c("new york", 10001); std::string n = get<name>(c); int z = get<zipcode>(c); }

Pero como dice Ben Voigt: struct city { string name; int zipcode; }; struct city { string name; int zipcode; }; Sería casi siempre mejor.

EDITAR: Las plantillas probablemente son una exageración, en su lugar podría usar funciones gratuitas en un espacio de nombres. Esto todavía no resuelve los problemas de seguridad de tipo, ya que cualquier std::pair<T1, T2> es del mismo tipo que cualquier otro std::pair<T1, T2> :

namespace city { typedef std::pair<std::string /*name*/, int /*zipcode*/> type; std::string& name(type& city) { return city.first; } int& zipcode(type& city) { return city.second; } } int main() { city::type c("new york", 10001); std::string n = city::name(c); int z = city::zipcode(c); }


Como std::pair se usa comúnmente para almacenar entradas en contenedores std::map , es posible que desee ver los elementos etiquetados en Boost Bimap .

Sinopsis:

#include <boost/bimap/bimap.hpp> #include <string> #include <iostream> struct name {}; // Tag for the default ''first'' member struct zipcode {}; // Tag for the default ''second'' member int main() { using namespace boost::bimaps; typedef bimap <tagged<std::string, name>, tagged<int, zipcode> > Cities; typedef Cities::value_type registration; Cities cities; cities.insert(registration("Amsterdam", 20)); cities.insert(registration("Rotterdam", 10)); // ... std::string cityName; std::cin >> cityName; Cities::map_by<name>::const_iterator id_iter = cities.by<name>().find(cityName); if( id_iter != cities.by<name>().end() ) { std::cout << "name: " << id_iter->get<name>() << std::endl << "zip: " << id_iter->get<zipcode>() << std::endl; } return 0; }

Tenga en cuenta que los bimaps pueden emular de forma transparente std::map u otros tipos de contenedores asociativos sin costo de rendimiento; Simplemente son más flexibles. En este ejemplo particular, lo más probable es que la definición se cambie en algo como:

typedef bimap <tagged<std::string, name>, multiset_of<tagged<int, zipcode> > > Cities; typedef Cities::value_type registration; Cities cities; cities.insert(registration("Amsterdam", 20)); cities.insert(registration("Rotterdam", 10)); cities.insert(registration("Rotterdam", 11));

Te invito a recorrer la documentación de Boost Bimap para obtener la imagen completa.


Creo que deberías introducir nuevos tipos aquí. Estoy totalmente del lado de stl en cuanto a tener una clase de pareja, pero esta es la razón exacta por la que las personas java argumentan que no quieren tener una clase de pareja y siempre debes introducir nuevos tipos para tus tipos tipo piar.

Lo bueno de la solución stl es que puede usar el par de clases gerneric, pero puede introducir nuevos tipos / clases siempre que quiera que los miembros se nombren de una forma diferente a la primera / segunda. Además de eso, la introducción de nuevas clases le brinda la libertad de agregar fácilmente un tercer miembro en caso de que sea necesario.


Le alegrará saber que la propuesta de rangos también incluye algo llamado tagged_pair , de modo que su:

struct city { string name; int zipcode; };

También se puede escribir como:

using city = tagged_pair<tag::name(std::string), tag::zipcode(int)>; city c{"Chicago", 60654}; std::cout << c.name() << " is at zipcode " << c.zipcode() << ''/n'';

Por supuesto, esto también se puede utilizar en el tipo de retorno directamente como normal:

tagged_pair<tag::min(int), tag::max(int)> get_range() { return {0, 100}; } auto score_range = get_range(); std::cout << "From " << score_range.min() << " to " << score_range.max();


No veo cómo posiblemente puedas hacerlo mejor que

struct city { string name; int zipcode; };

No hay nada que no sea esencial allí. Necesita los tipos de los dos miembros, toda su pregunta se basa en dar nombres a los dos miembros y desea que sea un tipo único.

Usted sabe acerca de la sintaxis de inicialización agregada, ¿verdad? No necesita un constructor o destructor, los que proporciona el compilador están bien.

Ejemplo: http://ideone.com/IPCuw

La seguridad de los tipos requiere que introduzcas nuevos tipos, de lo contrario pair<string, int> es ambiguo entre (nombre, código postal) y (población, temperatura).

En C ++ 03, devolver una tupla nueva requiere:

city retval = { "name", zipcode }; return retval;

o escribiendo un constructor de conveniencia:

city::city( std::string newName, int newZip ) : name(newName), zipcode(newZip) {}

Llegar

return city("name", zipcode);

Con C ++ 0x, sin embargo, se le permitirá escribir

return { "name", zipcode };

y ningún constructor definido por el usuario es necesario.


Puede utilizar operadores de puntero a miembro. Hay algunas alternativas. Aquí es lo más sencillo.

typedef std::map< zipcode_t, std::string > zipmap_t; static zipcode_t const (zipmap_t::value_type::*const zipcode) = &zipmap_t::value_type::first; static std::string (zipmap_t::value_type::*const zipname) = &zipmap_t::value_type::second; // Usage zipmap_t::value_type my_map_value; std::string &name = my_map_value.*zipname;

Puede colocar los accesores para un pseudo tipo en un namespace de namespace dedicado para separarlos de otras cosas. Entonces se vería como my_map_value.*zip::name . Pero, a menos que realmente necesite usar un pair , es probable que sea más fácil simplemente definir una nueva struct .


Se me ocurrió una macro Utility_pair que se puede usar así:

Utility_pair(ValidityDateRange, time_t, startDay, time_t, endDay );

Luego, cuando necesite acceder a los campos de ValidityDateRange , puede hacerlo de la siguiente manera:

ValidityDateRange r = getTheRangeFromSomewhere(); auto start = r.startDay(); // get the start day r.endDay() = aNewDay(); // set the end day r.startDay(aNewDay1()) // set the start and end day in one go. .endDay(aNewDay2());

Esta es la implementación:

#include <utility> #define Utility_pair_member_(pairName, ordinality, type, name) / const type &name() const { return ordinality; } / type &name() { return ordinality; } / pairName &name(const type &m) { ordinality = m; return *this; } / /***/ #define Utility_pair(pairName, firstMemberType, firstMemberName, secondMemberType, secondMemberName) / struct pairName: std::pair<firstMemberType, secondMemberType> { / Utility_pair_member_(pairName, first, firstMemberType, firstMemberName) / Utility_pair_member_(pairName, second, secondMemberType, secondMemberName) / } / /***/


Supongo que elaborando en

struct City : public std::pair<string, int> { string& name() { return first; } const string& name() const { return first; } int& zip() { return second; } int zip() const { return second; } };

es lo más cercano a lo que está buscando, a través de la struct City { string name; int zipcode; } struct City { string name; int zipcode; } struct City { string name; int zipcode; } parece perfectamente bien.


quizás pueda heredar su propia clase de par de pares y establecer dos referencias llamadas nombre y código postal en su constructor (solo asegúrese de implementar todos los constructores que usará)