根据我正在尝试让一个函数接受来自不同容器类型的迭代器。与该问题不同,我希望这些容器仅具有特定类型的元素。但是,如果我使用以下代码,我将失去控制元素类型的能力:

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

  • 1
    std::is_arithmetic<decltype(*it)>::value几乎肯定不是您想要的。通常decltype(*it)是引用类型,不能是算术类型。此外,*it甚至可能不是对值类型的引用。它可能是例如仅转换为值类型的代理类型。您需要弄清楚是否要检查std::iter_value_tstd::iter_reference_t


    – 


  • “因为我得到的错误是任何东西,但理解起来却很简单。” – 您是否尝试将错误消息从“非算术”更改为“传递给函数 f 的迭代器必须包含算术元素”? 您几乎可以完全控制错误消息。


    – 


最佳答案
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) {
    }
}