我编写了一个与iota非常相似的范围工厂,但生成用户定义类型的实例。简介如下:
struct MyType;
struct MyIterator {
using value_type = MyType;
using iterator_concept = std::input_iterator_tag;
// ...
auto operator*() const -> MyType;
};
struct MyView : public std::ranges::view_interface<MyView> {
auto begin() const -> MyIterator;
auto end() const -> MyIterator;
};
我已经尽了最大努力让这个遵循iota_view的设计。然而,我发现
MyView
不是一个恒定范围:
static_assert( std::ranges::constant_range<std::ranges::iota_view<int>>);
static_assert(!std::ranges::constant_range<MyView>); // Not a constant_range!
看了之后
the reference
,我追溯到
MyIterator
不是
constant-iterator
因为
-
std::same_as<std::iter_const_reference_t<MyIterator>, std::iter_reference_t<MyIterator>>
不成立是因为
-
std::iter_reference_t<MyIterator>
是
MyType
但是
std::iter_const_reference_t<MyIterator>
是
const MyType
.
如果我更换
MyType
具有
int
,就像
iota_view
,则两者都是非常量。
所以我想弄清楚区别是从哪里来的,结果发现
std::iter_const_reference_t
定义为
std::common_reference_t
,已被广泛覆盖
here
。反过来,它有一个复杂的策略来计算结果类型,但实际上两者都有
int
和
MyType
属于同一分支,即(参见
cppreference
):
template<class T> T val();
decltype(false? val<const T&&>() : val<T>())
事实证明这是
int
如果
T
是
int
和
const MyType
如果
T
是
MyType
.
我不知道为什么条件运算符解析这样的类型,但无论如何我都无法更改它;但幸运的是,该标准允许我通过专门化来注入自己的行为
std::basic_common_reference
,例如,我可以这样做:
template<template<class> class TQual, template<class> class UQual>
struct std::basic_common_reference<MyType, MyType, TQual, UQual>
{
using type = std::common_type_t<TQual<MyType>, UQual<MyType>>;
};
事实上,这解决了这个问题。但我不知道这是否正确,甚至不知道是否打算这样使用。一般来说,这似乎不太实用,因为没有具体的范围
MyType
-它恰好是我正在生成的类型,在我的真实用例中,它是视图的模板参数。这意味着视图是否符合constant_view完全取决于它所模板化的类型是否具有此basic_common_reference专门化。。。这似乎很奇怪。
供您参考,这里有
godbolt
链接
最后,这里有一个问题:用左值引用类型实现constant_view的正确方法是什么?感谢您的真知灼见。