我想写town->first
而不是写town->name
。内联命名访问器(Renaming first and second of a map iterator和Named std::pair members)是迄今为止我找到的最佳解决方案。我对命名访问器的问题是类型安全性的损失:
pair<int,double>
可以引用struct { int index; double value; }
或struct { int population; double avg_temp; }
。任何人都可以提出一个简单的方法,也许类似于特征吗?
我经常想从一个函数中返回一对或一个元组,每次引入一个像struct city { string name; int zipcode; }
这样的新类型及其ctor是非常累人的。我很高兴能够了解boost和C ++ 0x但我需要一个没有提升的纯C ++ 03解决方案。
更新
Re andrewdski的问题:是的,像({1}}这样的(假设的)语法可以创建一个来自pair<int=index, double=value>
的独特类型,符合您的要求。我甚至不介意实现一个自定义的对/元组模板类ONCE,并且当我需要一个新类型时,只是将'name traits'模板参数传递给它approprietly。我不知道“名称特征”会是什么样子。也许这是不可能的。
答案 0 :(得分:18)
我看不出你怎么能做得比
更好struct city { string name; int zipcode; };
那里没有什么不必要的。你需要这两个成员的类型,你的整个问题都是以给两个成员的名字命名,你希望它是一个独特的类型。
你知道聚合初始化语法,对吗?你不需要构造函数或析构函数,编译器提供的就好了。
类型安全要求您引入新类型,否则pair<string, int>
在(名称,邮政编码)和(人口,临时)之间是不明确的。
在C ++ 03中,返回一个新元组需要:
city retval = { "name", zipcode };
return retval;
或编写便利构造函数:
city::city( std::string newName, int newZip ) : name(newName), zipcode(newZip) {}
获取
return city("name", zipcode);
但是,使用C ++ 0x,您将被允许编写
return { "name", zipcode };
并且不需要用户定义的构造函数。
答案 1 :(得分:5)
虽然不完美,但可以使用标记数据:
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);
}
但正如Ben Voigt所说:struct city { string name; int zipcode; };
几乎总会更好。
编辑:模板可能有点过分,你可以在命名空间中使用自由函数。这仍然无法解决类型安全问题,因为任何std::pair<T1, T2>
与任何其他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);
}
答案 2 :(得分:3)
由于std::pair
通常用于在std::map
容器中存储条目,因此您可能需要查看tagged elements in Boost Bimap。
梗概:
#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;
}
请注意,bimaps可以透明地模拟std::map
或其他关联容器类型而不会降低性能;他们只是更灵活。在这个特定的例子中,定义最有可能最好改为:
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));
我邀请您浏览Boost Bimap的文档以获取完整的图片
答案 3 :(得分:3)
我想详细说明
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; }
};
是你最接近你想要的东西,通过struct City { string name; int zipcode; }
似乎完全没问题。
答案 4 :(得分:2)
您会很高兴知道the ranges proposal还包含一个名为tagged_pair
的内容,以便您:
struct city { string name; int zipcode; };
也可以写成:
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';
当然,这也可以直接用于返回类型:
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();
答案 5 :(得分:1)
您可以使用指向成员的运算符。还有一些选择。这是最直截了当的。
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;
您可以将一个伪类型的访问器放入专用的namespace
中,以将它们与其他内容分开。然后它看起来像my_map_value.*zip::name
。但是,除非您确实需要使用pair
,否则定义新的struct
可能会更容易。
答案 6 :(得分:1)
我想出了一个可以像这样使用的Utility_pair
宏:
Utility_pair(ValidityDateRange,
time_t, startDay,
time_t, endDay
);
然后,只要您需要访问ValidityDateRange
的字段,就可以这样做:
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());
这是实施:
#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) \
} \
/***/
答案 7 :(得分:0)
我认为你应该在这里引入新类型。在完成配对课程方面,我完全处于stl方面,但这就是为什么java人们认为他们不想拥有一对类而且你应该总是为类似piar的类型引入新类型的确切原因。
关于stl解决方案的好处是你可以使用泛型类对,但只要你真的希望以不同于第一个/第二个的方式命名成员,你就可以引入新的类型/类。除此之外,如果有必要,可以自由地添加一个thrid成员。
答案 8 :(得分:0)
可能不值得使用额外的内存,但是如果您想保留std::pair
或std::tuple
/的优势/保持与不变API的兼容性,则可以从它们继承,然后定义对其成员的引用
除了内存成本外,这还带来了一个巨大的警告,即STL类型通常没有虚拟析构函数,因此,如果您尝试使用指向基本类型的指针进行分配,则可能导致内存泄漏:
#include <tuple>
struct PairShadow : public std::pair<int, double> {
using std::pair<int, double>::pair;
PairShadow & operator=(PairShadow const &) { /*call parent op here*/ }
int & x = this->first;
double & y = this->second;
};
struct TupleShadow: public std::tuple<int, double> {
using std::tuple<int, double>::tuple;
PairShadow & operator=(PairShadow const &) { /*call parent op here*/ }
int & x = std::get<0>(*this);
double & y = std::get<1>(*this);
};
auto main() -> int {
auto twinShadow = PairShadow(1,3.0);
auto tupleShadow = TupleShadow(1,3.0);
return tupleShadow.y;
}
或者,正如@holyblackcat所提到的那样,在很多情况下,只要您不尝试遵循不可更改的API(特别要求使用API),也可以在很多情况下为结构定义std::get<int>(PairShadow)
的重载。 std::pair
或std::tuple
答案 9 :(得分:-1)
也许你可以从pair继承你自己的pair类,并在你的构造函数中设置两个名为name和zipcode的引用(只需确保实现你将使用的所有构造函数)