std::ranges::elements_view,用于自定义类似元组的数据

std::ranges::elements_view for custom tuple-like data

本文关键字:元组 数据 自定义 elements ranges view std 用于      更新时间:2023-10-16

我有一个用例可以简化为:

#include <vector>
#include <ranges>
#include <tuple>
struct TDat
{
double x, y;
template <std::size_t I>
friend double &get(TDat &Dat)
{ if constexpr (I == 0) return Dat.x; else return Dat.y; }
template <std::size_t I>
friend double const &get(TDat const &Dat)
{ if constexpr (I == 0) return Dat.x; else return Dat.y; }
};
namespace std
{
template <>
struct tuple_size<TDat> : integral_constant<size_t, 2u> {};
template <size_t I>
struct tuple_element<I, TDat>
{ using type = double; };
} // std
class TTable
{
private:
std::vector<TDat> DatVec;
public:  
auto GetxVec()
{ return std::views::keys(DatVec); }
};

这段代码不使用G++10编译,因为TDat不为__has_tuple_element<std::ranges::range_value_t<_Vp>, _Nm>建模。问题似乎是,这个概念被定义为(https://github.com/gcc-mirror/gcc/blob/fab263ab0fc10ea08409b80afa7e8569438b8d28/libstdc%2B%2B-v3/include/std/ranges#L3318(:

namespace __detail
{
template<typename _Tp, size_t _Nm>
concept __has_tuple_element = requires(_Tp __t)
{
typename tuple_size<_Tp>::type;
requires _Nm < tuple_size_v<_Tp>;
typename tuple_element_t<_Nm, _Tp>;
{ std::get<_Nm>(__t) }
-> convertible_to<const tuple_element_t<_Nm, _Tp>&>;
};
}

我相信,由于对get的调用是限定的,所以编译器找不到我的get函数。这个结论正确吗?如果是,这是预期行为吗?在std命名空间中定义get函数是定义行为吗?

我相信,由于对get的调用是限定的,所以编译器找不到我的get函数。这个结论正确吗?

是。

如果是,这是预期行为吗?

这至少是指定的行为。[range.elements.view]将概念定义为:

template<class T, size_t N>
concept has-tuple-element =                   // exposition only
requires(T t) {
typename tuple_size<T>::type;
requires N < tuple_size_v<T>;
typename tuple_element_t<N, T>;
{ get<N>(t) } -> convertible_to<const tuple_element_t<N, T>&>;
};

除非另有说明,否则标准库中的所有函数调用都是隐式完全限定的[library.contents]/3:

每当提到在标准库中定义的名称x时,除非另有明确说明,否则假定名称x完全限定为​::​std​::​x

此处没有其他说明。

然而,它是否有意是另一个问题。答案也是肯定的。图书馆实际上并没有一个";TupleLike";概念是的。是的,结构化绑定是存在的,但库中还没有对此进行正式规范——因此,在这之前,所有类似元组的算法都只适用于标准库的类似元组的东西(pairarraytuple等(

在std命名空间中定义get函数是定义行为吗?

不,你可能不会。