带有运算符的 std::array 的 C++11 类型别名
C++11 type alias of std::array with operators
我有一个 c++11 类型别名:
using coord = std::array<double, 3>;
我可以为 coord 定义运算符 + 吗?如何?我希望能够做到:
coord a, b, c;
a = {1.1, 2.0, 0};
b = {0, -1, 3.5};
c = a + b; // c = {1.1, 1.0, 3.5}
我不建议为 STL 类型或您无法控制的任何类型定义新的运算符。
我建议改为创建您自己的类型:
struct coord {
// reimplement operator[], fill, size, etc.
friend constexpr auto operator+ (coord const& lhs, coord const& rhs) noexcept -> coord {
// ...
}
private:
std::array<double, 3> _coord;
};
或者您也可以使用已经在 std::array
中定义的运算符和成员函数来绕过捷径:
struct coord : private std::array<double, 3> {
constexpr coord(double a, double b, double c) noexcept :
array{a, b, c} {}
using array::operator[];
using array::begin;
using array::end;
using array::fill;
// other operators...
friend constexpr auto operator+ (coord const& lhs, coord const& rhs) noexcept -> coord {
// ...
}
};
注意:如果您想要支持结构化绑定并提供类似元组的访问您的 coord
class,那么您必须为 get
添加重载并专门化 std::tuple_size
和 std::tuple_element
:
namespace std {
template<size_t n>
tuple_element<n, ::coord> {
using type = double;
};
tuple_size<::coord> : integral_constant<size_t, 3> {};
}
template<std::size_t n>
constexpr auto get(coord const& c) noexcept -> double const& {
return c[n]
}
template<std::size_t n>
constexpr auto get(coord& c) noexcept -> double& {
return c[n];
}
template<std::size_t n>
constexpr auto get(coord&& c) noexcept -> double&& {
return std::move(c[n]);
}
template<std::size_t n>
constexpr auto get(coord const&& c) noexcept -> double const&& {
return std::move(c[n]);
}
当您打开 C++17 时,此代码将允许此类代码:
auto my_coord = coord{1.0, 2.0, 3.0};
auto [x, y, z] = my_coord;
我想补充为什么你不应该为 std::array
添加 operator+
。或者,更一般地说,为什么你应该只将运算符(或者更确切地说,自由函数)添加到它们所操作的类型的命名空间(在 std
的情况下是禁止的)。
首先,您不能将运算符放在命名空间 A
中,然后在命名空间 B
中正确使用它(没有 using namespace A;
):
namespace A
{
using coord = std::array<double, 3>; // Putting this in the global namespace doesn't help.
coord operator+(const coord& right, const coord& left)
{
return coord { right[0] + left[0], right[1] + left[1], right[2] + left[2] };
}
}
auto foo()
{
A::coord x, y;
return x + y; // doesn't see the operator
}
唯一可以放置运算符的地方是全局命名空间。如果这不会让您感到不舒服,那么它应该有一个原因:名称查找规则很快就会让您扭断脖子。
如果我们想编写一个使用您的 operator+
的模板怎么办?应该没问题吧?
template<class ... T>
auto add(T ... t)
{
return (t + ...);
}
是的,它有效!好吧,直到有人添加 any operator+
更接近模板:
namespace A
{
struct X{};
void operator+(X, X);
template<class ... T>
auto add(T ... t)
{
return (t + ...); // won't find the correct operator+ for T=coord
}
}
名称查找在 operator+
处停止。由于您正确的(对于 std::array
)不在 namespace std
(std::array
所在的位置),因此 ADL(参数相关查找)无法找到它。到那时你就没有选择了。
我有一个 c++11 类型别名:
using coord = std::array<double, 3>;
我可以为 coord 定义运算符 + 吗?如何?我希望能够做到:
coord a, b, c;
a = {1.1, 2.0, 0};
b = {0, -1, 3.5};
c = a + b; // c = {1.1, 1.0, 3.5}
我不建议为 STL 类型或您无法控制的任何类型定义新的运算符。
我建议改为创建您自己的类型:
struct coord {
// reimplement operator[], fill, size, etc.
friend constexpr auto operator+ (coord const& lhs, coord const& rhs) noexcept -> coord {
// ...
}
private:
std::array<double, 3> _coord;
};
或者您也可以使用已经在 std::array
中定义的运算符和成员函数来绕过捷径:
struct coord : private std::array<double, 3> {
constexpr coord(double a, double b, double c) noexcept :
array{a, b, c} {}
using array::operator[];
using array::begin;
using array::end;
using array::fill;
// other operators...
friend constexpr auto operator+ (coord const& lhs, coord const& rhs) noexcept -> coord {
// ...
}
};
注意:如果您想要支持结构化绑定并提供类似元组的访问您的 coord
class,那么您必须为 get
添加重载并专门化 std::tuple_size
和 std::tuple_element
:
namespace std {
template<size_t n>
tuple_element<n, ::coord> {
using type = double;
};
tuple_size<::coord> : integral_constant<size_t, 3> {};
}
template<std::size_t n>
constexpr auto get(coord const& c) noexcept -> double const& {
return c[n]
}
template<std::size_t n>
constexpr auto get(coord& c) noexcept -> double& {
return c[n];
}
template<std::size_t n>
constexpr auto get(coord&& c) noexcept -> double&& {
return std::move(c[n]);
}
template<std::size_t n>
constexpr auto get(coord const&& c) noexcept -> double const&& {
return std::move(c[n]);
}
当您打开 C++17 时,此代码将允许此类代码:
auto my_coord = coord{1.0, 2.0, 3.0};
auto [x, y, z] = my_coord;
我想补充为什么你不应该为 std::array
添加 operator+
。或者,更一般地说,为什么你应该只将运算符(或者更确切地说,自由函数)添加到它们所操作的类型的命名空间(在 std
的情况下是禁止的)。
首先,您不能将运算符放在命名空间 A
中,然后在命名空间 B
中正确使用它(没有 using namespace A;
):
namespace A
{
using coord = std::array<double, 3>; // Putting this in the global namespace doesn't help.
coord operator+(const coord& right, const coord& left)
{
return coord { right[0] + left[0], right[1] + left[1], right[2] + left[2] };
}
}
auto foo()
{
A::coord x, y;
return x + y; // doesn't see the operator
}
唯一可以放置运算符的地方是全局命名空间。如果这不会让您感到不舒服,那么它应该有一个原因:名称查找规则很快就会让您扭断脖子。
如果我们想编写一个使用您的 operator+
的模板怎么办?应该没问题吧?
template<class ... T>
auto add(T ... t)
{
return (t + ...);
}
是的,它有效!好吧,直到有人添加 any operator+
更接近模板:
namespace A
{
struct X{};
void operator+(X, X);
template<class ... T>
auto add(T ... t)
{
return (t + ...); // won't find the correct operator+ for T=coord
}
}
名称查找在 operator+
处停止。由于您正确的(对于 std::array
)不在 namespace std
(std::array
所在的位置),因此 ADL(参数相关查找)无法找到它。到那时你就没有选择了。