【问题标题】:How to check if a container passed to a function is sorted and sort it if not如何检查传递给函数的容器是否已排序,如果没有排序
【发布时间】:2016-12-16 15:39:47
【问题描述】:

我有这个我们前段时间写的函数:

template <class C, class T>
static inline bool findClosestObject( const C& container, const TimeUnit& now, T& object );
  • C 是 T 元素的容器
  • TimeUnit 是一个封装日期和时间的类
  • T 是一个带有TimeUnit 信息的对象

此函数在容器中进行二分搜索(使用std::lower_bound)以找到最接近now 的对象。

当我们进行二分搜索时,必须对容器进行排序。此功能在很多地方与多种容器一起使用(C 可以是std::vectorstd::setstd::map...)。有时我们使用 sorted std::vector 而不是 std::set,因为它们的内存管理速度更快,并且还用于历史问题以及与使用向量的其他代码的兼容性。

问题是我在代码中找到了一个位置,其中一个名为 findClosestObject 的开发人员使用一个未排序的容器......很好的错误......我无法安全地识别所有可以这样做的地方。

所以我现在需要通过在这种不存在的特定情况下对容器进行排序来防止这种情况发生(会很慢,但至少可以工作并保证函数返回我们希望它返回的内容)

所以我尝试修改我的功能:

template <class C, class T>
static inline const C& fixOrder( const C& container, C& temp )
{
    if ( std::is_sorted( container.begin(), container.end() )
    {
        return container;
    }
    else
    {
        assert( false ); // to alert developper
        // in Release, fix the issue to have function work!
        temp = container;
        std::sort( temp.begin(), temp.end() );
        return temp;
    }
}

template <class C, class T>
static inline bool findClosestObject( const C& originalContainer, const TimeUnit& now, T& object )
{
    C temp;
    const C& container = fixOrder( originalContainer, temp );
    ...
    // leave old code unchanged
}

但是当Cstd::setstd::map 时编译失败。因为std::sort 不允许用于那种容器...

fixOrder 可以写成只为std::vector 做事而不为其他容器做事吗?

【问题讨论】:

  • 为什么不为std::vector&lt;T&gt;添加重载?

标签: c++ sorting c++11 vector stl


【解决方案1】:

您可以为模板函数添加 std::set&lt;T&gt;std::map&lt;T1,T2&gt; 的部分特化:

// This one is called for every C not having a better specialization
template <class C>
static inline const C& fixOrder( const C& container, C& temp )
{
    if ( std::is_sorted( container.begin(), container.end() ))
    {
        return container;
    }
    else
    {
        assert( false ); // to alert developper
        // in Release, fix the issue to have function work!
        temp = container;
        std::sort( temp.begin(), temp.end() );
        return temp;
    }
}

// This one is called for std::set<T>
template<class T>
static inline const set<T>& fixOrder( const set<T>& container, set<T>& temp )
{
    return container;
}

// This one is called for std::map<T1, T2>
template<class T, class T2>
static inline const map<T, T2>& fixOrder( const map<T, T2>& container, map<T, T2>& temp )
{
    return container;
}

This answer 有关于模板函数重载解析的详细信息。

【讨论】:

  • 顺便说一句,由于目标是保证容器总是被排序,所以让非专业版本进行排序可能更安全,而有专门版本的set/map 什么都不做(然后我们可以在需要时添加一些专门的版本,但我们保留默认行为以静默排序“全部”)。更新您的帖子以反映这一点可能很有意义。
  • @jpo38 同意,将修复
  • 不错。再次感谢您的帮助。
【解决方案2】:

alexeykuzmin's answer 更简单,应该可以解决您的问题。我下面的答案稍微复杂一些,但出于教育目的,它可能是一本有趣的读物。


可以将 fixOrder 编写成只对 std::vector 执行操作而不对其他容器执行操作的方式吗?

是的!你可以使用std::enable_if 和一个助手is_specialization_of trait:

template <typename, template <typename...> class>
struct is_specialization_of : std::false_type
{
};

template <template <typename...> class TTemplate, typename... Ts>
struct is_specialization_of<TTemplate<Ts...>, TTemplate> : std::true_type
{
};

template <class C>
static inline auto fixOrder(const C& x, C&)
    -> typename std::enable_if<is_specialization_of<C, std::vector>{}, const C&>::type
{
    std::cout << "C is a vector\n";
    return x;
}

template <class C>
static inline auto fixOrder(const C& x, C&)
    -> typename std::enable_if<!is_specialization_of<C, std::vector>{}, const C&>::type
{
    std::cout << "C is not a vector\n";
    return x;
}

用上面的代码...

int main() 
{
    std::vector<int> v;
    std::set<int> s;

    fixOrder(v, v);
    fixOrder(s, s);
}

...将打印:

C 是一个向量

C 不是向量

wandbox example

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2013-08-09
    • 2021-05-08
    • 2018-12-18
    • 2016-09-23
    • 2012-04-08
    • 2020-08-20
    • 2018-04-10
    • 1970-01-01
    相关资源
    最近更新 更多