Can I use std::pair, but rename .first and .second member names?

∥☆過路亽.° 提交于 2019-11-30 17:54:48

One way you could get around this is to use std::tie. You can tie() the return into variables that you have named so that you have good names.

int x_pos, y_pos;

std::tie(x_pos, y_pos) = function_that_returns_pair_of_cords();

// now we can use x_pos and y_pos instead of pair_name.first and pair_name.second

Another benefit with this is if you ever change the function to return a tuple tie() will also work with that.


With C++17 we now have structured bindings which allow you to declare and bind multiple variables to the return of the function. This work with arrays, tuple/pair like objects and struct/classes (as long as they meet some requirments). Using structured bindings in this case lets use convert the above example into

auto [x_pos, y_pos] = function_that_returns_pair_of_cords();

You can also do

auto& [x_pos, y_pos] = cords;

and now x_pos is a reference to cords.first and y_pos is a reference to cords.second.

You can just make free functions:

int& get_x(std::pair<int, int>& p) { return p.first; }
int& get_y(std::pair<int, int>& p) { return p.second; }
int const& get_x(std::pair<int, int> const& p) { return p.first; }
int const& get_y(std::pair<int, int> const& p) { return p.second; }

Eric Niebler's tagged might help here. The basic idea is that you create getters like this:

struct x_tag {
    template<class Derived, class Type, std::size_t N>
    struct getter {
        Type& x() & { 
            return std::get<N>(static_cast<Derived&>(*this)); 
        }
        Type&& x() && { 
            return std::get<N>(static_cast<Derived&&>(*this)); 
        }
        const Type& x() const & { 
            return std::get<N>(static_cast<const Derived&>(*this)); 
        }
        const Type&& x() const && { 
            return std::get<N>(static_cast<const Derived&&>(*this)); 
        }
    };
};

And you can similarly implement y_tag (just change the member function names to y()). Then:

template<class, class, class...> struct collect;
template<class Derived, std::size_t... Ns, class... Tags>
struct collect<Derived, std::index_sequence<Ns...>, Tags...>
      : Tags::template getter<Derived, std::tuple_element_t<Ns, Derived>, Ns>...{};

template<class Base, class... Tags>
struct tagged : Base, collect<tagged<Base, Tags...>, 
                              std::index_sequence_for<Tags...>, Tags...> {
    using Base::Base;
    // extra polish for swap and converting from other tagged's.
};

namespace std
{
    template<typename Base, typename...Tags>
    struct tuple_size<tagged<Base, Tags...>>
      : tuple_size<Base>
    {};

    template<size_t N, typename Base, typename...Tags>
    struct tuple_element<N, tagged<Base, Tags...>>
      : tuple_element<N, Base>
    {};
}

Then

using coord_t = tagged<std::pair<int, int>, x_tag, y_tag>;

You cannot rename std::pair's members, but you could create an equivalent class that has named variables. Instead of templates, you can fall back on #defines. You can have this declaration:

DefinePair(Dimensions, int, Height, int, Width);
Dimensions dimensions(3, 4);
cout << dimensions.mHeight;

which isn't the same as std::pair but gives you the ease of declaration that people want from std::pair while retaining the naming.

There are lots of ways to structure this -- you could inherit from std::pair and then expose the named variables as references to the first and second, if you need to plug into something that takes pairs. But the simplest implementation is something like this:

#define DefinePair(StructName, FirstType, FirstName, SecondType, SecondName)    \
    struct StructName { \
        FirstType m##FirstName; \
        SecondType m##SecondName; \
        StructName(FirstType FirstName, SecondType SecondName) \
            : m##FirstName(FirstName), \
            m##SecondName(SecondName) \
        {} \
    };

It would be nice if we could do this with C++ templates, but I know of no way to do it. It would require some sort of new keyword like "template " where identifier would mean "this template parameter is going to be used to name variables, types, or methods inside the template."

newbie

You can use

#define _px first   
#define _py second
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!