20

我遇到的一个常见设计问题是,我将两个变量捆绑在一起,然后失去以有意义的方式引用它们的能力。

std::pair<int,int> cords;
cord.first = 0; //is .first the x or y coordinate?
cord.second = 0; //is .second the x or y coordinate?

我考虑过编写基本结构,但是我失去了很多随之而来的好处std::pair

  • make_pair
  • 非成员重载运算符
  • 交换
  • 得到
  • 等等

first有没有办法为和second数据成员重命名或提供替代标识符?

我希望利用所有接受的功能std::pair
但仍然可以通过以下方式使用它们:

std::pair<int,int> cords;  
//special magic to get an alternative name of access for each data member.

//.first and .second each have an alternative name.
cords.x = 1;
assert(cords.x == cords.first);
4

5 回答 5

18

解决此问题的一种方法是使用std::tie. 您可以tie()返回到您已命名的变量中,以便您拥有好名字。

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

这样做的另一个好处是,如果您更改函数以返回元组tie()也可以使用它。


使用 C++17,我们现在有结构化绑定,允许您声明多个变量并将其绑定到函数的返回。这适用于数组、元组/配对对象和结构/类(只要它们满足一些要求)。在这种情况下使用结构化绑定可以将上面的示例转换为

auto [x_pos, y_pos] = function_that_returns_pair_of_cords();

你也可以做

auto& [x_pos, y_pos] = cords;

和 nowx_pos是对cords.firsty_pos的引用cords.second

于 2015-09-15T16:13:40.753 回答
6

您可以制作免费功能:

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; }
于 2015-09-15T16:28:31.717 回答
5

Eric Niebler 的tagged可能会有所帮助。基本思想是您创建这样的吸气剂:

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

您可以类似地实现y_tag(只需将成员函数名称更改为y())。然后:

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

然后

using coord_t = tagged<std::pair<int, int>, x_tag, y_tag>;
于 2015-09-15T21:25:27.167 回答
0

您不能重命名 std::pair 的成员,但可以创建具有命名变量的等效类。您可以使用#defines 代替模板。你可以有这个声明:

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

这与 std::pair 不同,但可以让您轻松声明人们希望从 std::pair 获得的声明,同时保留命名。

有很多方法可以构造它——你可以从 std::pair 继承,然后将命名变量公开为对第一个和第二个的引用,如果你需要插入需要对的东西。但最简单的实现是这样的:

#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) \
        {} \
    };

如果我们可以用 C++ 模板做到这一点,那就太好了,但我不知道怎么做。它需要某种新的关键字,例如“模板”,其中标识符的意思是“此模板参数将用于命名模板内的变量、类型或方法”。

于 2018-05-08T16:07:18.713 回答
-2

您可以使用

#define _px first   
#define _py second
于 2017-06-26T11:55:16.247 回答