(重新)命名 std::pair 成员

发布于 2024-11-08 17:46:08 字数 905 浏览 3 评论 0 原文

我不想写 town->first 我想写 town->name。内联命名访问器(重命名地图迭代器的第一个和第二个命名 std::pair 成员)是迄今为止我发现的最佳解决方案。我对命名访问器的问题是类型安全性的丧失: pair 可以参考struct { int index;双值; }struct { int Population;双平均温度; }。任何人都可以提出一种简单的方法,也许类似于特征的方法?

我经常想从函数返回一个对或一个元组,引入像 struct city { string name; 这样的新类型是相当累人的。 int 邮政编码; } 及其每次的构造函数。我很高兴了解 boost 和 C++0x,但我需要一个没有 boost 的纯 C++03 解决方案。

更新

关于andrewdski的问题:是的,像pair这样的(假设的)语法将创建与pair 可以满足您的要求。我什至不介意必须实现一次自定义对/元组模板类,并在需要新类型时适当地向其传递“名称特征”模板参数。我不知道“名字特征”会是什么样子。也许这是不可能的。

Instead of writing town->first I would like to write town->name. Inline named accessors (Renaming first and second of a map iterator and Named std::pair members) are the best solutions I have found so far. My problem with named accessors is the loss of type safety:
pair<int,double> may refer to struct { int index; double value; } or to struct { int population; double avg_temp; }. Can anyone propose a simple approach, perhaps something similar to traits?

I often want to return a pair or a tuple from a function and it is quite tiring to introduce a new type like struct city { string name; int zipcode; } and its ctor every time. I am thrilled to learn about boost and C++0x but I need a pure C++03 solution without boost.

Update

Re andrewdski's question: yes, a (hypothetical) syntax like pair<int=index, double=value> which would create a distinct type from pair<int=population, double=avg_temp> would meet your requirement. I do not even mind having to implement a custom pair/tuple template class ONCE and just passing a 'name traits' template argument to it approprietly when I need a new type. I have no idea how that 'name traits' would look like. Maybe it's impossible.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(10

过期情话 2024-11-15 17:46:09

我不知道你还能做得比

struct city { string name; int zipcode; };

“没有什么是不重要的”更好。您需要两个成员的类型,您的整个问题是围绕为两个成员命名而构建的,并且您希望它是唯一的类型。

您确实了解聚合初始化语法,对吗?您不需要构造函数或析构函数,编译器提供的就可以了。

示例:http://ideone.com/IPCuw


类型安全要求您引入新类型,否则pair 在 (name, zipcode) 和 (population, temp) 之间不明确。

在 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 };

并且不需要用户定义的构造函数。

I don't see how you can possibly do better than

struct city { string name; int zipcode; };

There's nothing non-essential there. You need the types of the two members, your whole question is predicated around giving names to the two members, and you want it to be a unique type.

You do know about aggregate initialization syntax, right? You don't need a constructor or destructor, the compiler-provided ones are just fine.

Example: http://ideone.com/IPCuw


Type safety requires that you introduce new types, otherwise pair<string, int> is ambiguous between (name, zipcode) and (population, temp).

In C++03, returning a new tuple requires either:

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

or writing a convenience constructor:

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

to get

return city("name", zipcode);

With C++0x, however, you will be allowed to write

return { "name", zipcode };

and no user-defined constructor is necessary.

第几種人 2024-11-15 17:46:09

我想详细说明

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 邮政编码; } 看起来完全没问题。

I guess elaborating on

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; }
};

is the closest you get to what youre looking for, althrough struct City { string name; int zipcode; } seems perfectly fine.

一个人的旅程 2024-11-15 17:46:09

虽然不完美,但可以使用标记数据:

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 邮政编码; }; 几乎总是会更好。

编辑:模板可能是一种矫枉过正,您可以在命名空间中使用自由函数。这仍然无法解决类型安全问题,因为任何 std::pair 与任何其他 std::pair 的类型相同:

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);
}

Although not perfect, it is possible to use tagged data:

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);
}

But as Ben Voigt says: struct city { string name; int zipcode; }; would pretty much always be better.

EDIT: Templates probably are an overkill, you could use free functions in a namespace instead. This still does not solve type safety issues, as any std::pair<T1, T2> are the same type as any other 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);
}
人生百味 2024-11-15 17:46:09

由于 std::pair 通常用于在 std::map 容器中存储条目,因此您可能需要查看 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;
}

请注意,bimap 可以透明地模拟 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 的文档以了解完整情况

Since std::pair is commonly used for storing entries in std::map containers, you might want to look at tagged elements in Boost Bimap.

Synopsis:

#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;
}

Note that bimaps can transparently emulate std::map or other associative container types without performance cost; They just are more flexible. In this particular example, the definition would most likely best be changed into something like:

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));

I invite you to wander around the documentation for Boost Bimap to get the full picture

月依秋水 2024-11-15 17:46:09

您可以使用指向成员的指针运算符。有几种选择。这是最直接的。

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;

您可以将一种伪类型的访问器放入专用的命名空间中,以将它们与其他事物分开。那么它看起来像my_map_value.*zip::name。但是,除非您确实需要使用pair,否则定义一个新的struct可能会更容易。

You can use pointer-to-member operators. There are a few alternatives. Here is the most straightforward.

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;

You can put the accessors for one pseudo-type into a dedicated namespace to separate them from other things. Then it would look like my_map_value.*zip::name. But, unless you really need to use a pair, it's probably easier to just define a new struct.

潜移默化 2024-11-15 17:46:09

我想出了一个 Utility_pair 宏,可以这样使用:

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

时,您可以这样做:

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());

然后,每当您需要访问 ValidityDateRange 的字段 执行:

#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)                   \
    }                                                                                                \
/***/

I came up with a Utility_pair macro that can be used like this:

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

Then, whenever you need to access the fields of ValidityDateRange, you can do it like this:

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());

This is the implementation:

#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)                   \
    }                                                                                                \
/***/
一花一树开 2024-11-15 17:46:09

可能不值得额外的内存,但如果您想保留 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,您也可以为您的结构定义 std::get(PairShadow) 的重载需要 std::pairstd::tuple

Probably not worth the extra memory, but if you want to retain the advantage of std::pair or std::tuple/ maintain compatibility with an unchangeable API, you can inherit from them and then define references to their members.

Apart from the memory cost, this comes with the giant caveat that STL types generally don't have virtual destructors and can thus cause memory leaks if you attempt to de-allocate using a pointer to the base type:

#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;
}

Alternatively, and perhaps preferrably as mentioned by @holyblackcat you could also just define an overload to std::get<int>(PairShadow) for your struct in many cases as long as you aren't trying to conform to an unchangable API which specifically requires a std::pair or std::tuple

怂人 2024-11-15 17:46:09

老问题,但添加了 C++17 的更新,您可以在其中使用 结构化绑定声明< /a> 用于命名 std::pair 和 std::tuple 的成员。例如:

std::list<std::pair<std::string, long>> getCitiesAndPopulations();
. . . 
for(const auto& [city, population] : getCitiesAndPopulations())
    std::cout << "City: " << city << ", population: " << population << std::endl;

注意:这不涉及类型安全方面。

Old question, but adding an update for C++17 where you can use Structured binding declaration for naming the members of std::pair and std::tuple. For example:

std::list<std::pair<std::string, long>> getCitiesAndPopulations();
. . . 
for(const auto& [city, population] : getCitiesAndPopulations())
    std::cout << "City: " << city << ", population: " << population << std::endl;

Note: This doesn't address the type safety aspect.

星星的轨迹 2024-11-15 17:46:09

我认为你真的应该在这里引入新类型。在拥有pair类方面,我完全站在stl一边,但这正是java人认为他们不想拥有pair类的确切原因,并且你应该总是为你的类似piar的类型引入新类型。

stl 解决方案的好处是您可以使用通用类对,但只要您确实希望以与第一/第二不同的方式命名成员,您就可以引入新的类型/类。除此之外,引入新类还可以让您在必要时轻松添加第三个成员。

I think you should really introduce new types here. I am totally on stl's side in terms of having a pair class, but this is the exact reason why java people argue they don't want to have a pair class and you should always introduce new types for your piar-like types.

The good thing about the stl solution is that you can use the gerneric class pair, but you can introduce new types / classes whenever you really want members to be named in a different way than first/second. On top of that introducing new classes gives you the freedom of easily adding a thrid member if it should ever become necessary.

暖伴 2024-11-15 17:46:09

也许您可以从pair继承您自己的pair类,并在构造函数中设置两个名为name和zipcode的引用(只需确保实现您将使用的所有构造函数)

perhaps you can inherit your own pair class from pair and set two references called name and zipcode in your constructor ( just be sure to implement all constructors you will use )

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文