根据我正在尝试让一个函数接受来自不同容器类型的迭代器。与该问题不同,我希望这些容器仅具有特定类型的元素。但是,如果我使用以下代码,我将失去控制元素类型的能力:
template<std::input_iterator ITER>
void f(ITER start, ITER end)
{
for(auto it=start; it!=end; ++it)
{
static_assert(std::is_arithmetic<decltype(*it)>::value, "Not arithmetic");
}
}
这里static_assert
不是一个非常糟糕的解决方案,因为我得到的错误是什么,但很容易理解。
想象main
一下我的
int main()
{
std::vector<int> v_good{{1, 2, 3, 4, 5}};
std::vector<std::string> v_bad{{std::string("hello"), std::string("world")}};
std::list<int> l_good{{1, 2, 3, 4, 5}};
f(v_good.begin(), v_good.end());
f(l_good.begin(), l_good.end());
f(v_bad.begin(), v_bad.end()); // at this point i want to get a compilation error
return 0;
}
2
最佳答案
2
您可以通过以下方式将value_type
迭代器(std::iter_value_t
)的约束为算术类型
template<std::input_iterator ITER>
requires std::is_arithmetic_v<std::iter_value_t<ITER>>
void f(ITER start, ITER end)
{
for (auto it=start; it!=end; ++it)
{
// ...
}
}
在 C++20 中,范围的返回类型end()
(即标记类型)可能与其不同begin()
,因此你可能还需要
template<std::input_iterator ITER, std::sentinel_for<ITER> SENT>
requires std::is_arithmetic_v<std::iter_value_t<ITER>>
void f(ITER start, SENT end)
{
for(auto it=start; it!=end; ++it)
{
// ...
}
}
1
-
我认为这里的主要优点是重载;可以使用能够处理非算术迭代范围的
f
重载来实现。
–
|
您可以将is_arithmetic
需求移出到concept
其自己的部分中,如下所示iterator_over_arithmetic_type
:
template <class ITER>
concept iterator_over_arithmetic_type =
std::input_iterator<ITER> &&
std::is_arithmetic_v<std::iter_value_t<ITER>>;
template <iterator_over_arithmetic_type ITER>
void f(ITER start, ITER end) {
for (auto it = start; it != end; ++it) {
}
}
|
std::is_arithmetic<decltype(*it)>::value
几乎肯定不是您想要的。通常decltype(*it)
是引用类型,不能是算术类型。此外,*it
甚至可能不是对值类型的引用。它可能是例如仅转换为值类型的代理类型。您需要弄清楚是否要检查std::iter_value_t
或std::iter_reference_t
。–
–
|