【问题标题】:How to properly deduce a dereferenced type of the iterator returned from std::set::begin()?如何正确推断从 std::set::begin() 返回的迭代器的取消引用类型?
【发布时间】:2019-05-04 18:56:50
【问题描述】:

我有一个模板类,其容器类型定义为 StorageType 模板。

StorageType mStorage;
....
void iterate( const std::function<bool( typename StorageType::value_type& )>& aFnc )
{
    for( auto it = mStorage.begin(); it != mStorage.end(); ++it )
    {
        aFnc( *it );
    }
}

这适用于大多数 stl 容器。但是当谈到 std::set 我面临一个问题,因为 mStorage.begin() 返回 const_iterator 并且 aFnc( *it ) 编译失败,因为 aFnc 函数需要 StorageType::value_type& 这是非常量的。

我试图通过获取 std::set::begin 的结果并推断其接收的迭代器的取消引用类型来推断正确的类型,看起来像:

void iterate( std::function<void(typename std::iterator_traits<std::result_of<decltype(&std::set<ValueType>::begin)(std::set<ValueType>)>::type>::value_type& )>& aFnc )

但似乎 std::iterator_traits::value_type 返回非常量值类型,不管来自 std::set::begin() 的真正迭代器取消引用 const T&。

我的目标是推断出作为参数提供的函数的正确签名。如果我将提到的函数定义为 ( const std::function& aFnc ),一切都将为 std::set 编译,但不适用于向量等其他容器类型。

【问题讨论】:

  • StorageType的定义是什么?
  • A std::set 不允许就地修改其元素。从 C++17 开始,您可以使用 extract 成员函数,但这是一种不同的方法,与您发布的方法不匹配。无论如何,您什么时候想交替注入std::vectorstd::set?我没有看到一个合理的用例,它们是完全不同的容器。
  • @MatthieuBrucher 这是一个模板参数。
  • 确实应该是一个const&,而不仅仅是一个&...
  • @lubgr 我知道它不允许修改。我的目标是推断作为参数提供的函数的正确签名。如果我将其定义为 ( const std::function& aFnc ),一切都会为 set 编译,但不适用于其他容器类型,如 vector。

标签: c++


【解决方案1】:

std::set 不允许通过迭代器修改元素。 std::set::iteratorstd::set::const_iterator 都是常量迭代器(甚至可能是同一类型)。这是因为std::set 必须不允许重复。

所以无论是什么类型的推演巫术都根本不可能在这种for迭代中修改set的元素。如果aFnc需要修改元素你不能使用std::set

如果 aFnc 不修改其参数,那么您需要将 arg 设为 const:

using StoredType = typename StorageType::value_type;

void iterate(const std::function<bool(const StoredType&)>& aFnc)

【讨论】:

    【解决方案2】:

    这里不需要std::function。只需让aFnc 成为推导的模板参数。

    template <typename Func>
    void iterate( Func aFnc )
    {
        for( auto & obj : mStorage )
        {
            aFnc( obj );
        }
    }
    

    或者

    template <typename Func>
    void iterate( Func && aFnc )
    {
        std::for_each( mStorage.begin(), mStorage.end(), std::forward<Func>(aFnc) );
    }
    

    【讨论】:

    • 这确实是一个解决方案。我已经在很多地方使用了这个功能,如果我要引入模板参数,我将不得不更改数百个地方。
    • @teoring 不,您不需要更改呼叫站点。可以推导出函数模板参数。
    • 我不应该将函数调用从 iterate( aFnc) 更改为 iterate( aFnc ) 以进行编译吗?我想我会的。
    • @teoring 不,你不需要。 iterate&lt;&gt;( aFnc ) 在该上下文中的含义与 iterate( aFnc) 相同。
    • 你是绝对正确的。这对我来说是一个可以接受的解决方案。出于某种原因,虽然我将不得不指定一个模板括号。
    猜你喜欢
    • 2017-03-10
    • 1970-01-01
    • 1970-01-01
    • 2012-09-13
    • 1970-01-01
    • 2020-09-13
    • 1970-01-01
    • 1970-01-01
    • 2013-09-23
    相关资源
    最近更新 更多