如何实现接受任何容器类型的功能?

Spa*_*ska 1 c++ c++11

我想实现一个带有std::vector或std::array作为参数的函数。参数列表如何从容器类型中抽象出来?

请参阅以下示例:

// how to implement this?
bool checkUniformity(container_type container)
{
    for(size_t i = 1; i < container.size(); i++)
    {
        const auto& o1 = container[i-1];
        const auto& o2 = container[i];

        if(!o1.isUniform(o2))
            return false;
    }

    return true;
}

struct Foo
{
    bool isUniform(const Foo& other);
}

// I want to call it in both ways:
std::vector<Foo> vec;
std::array<Foo> arr;

bool b1 = checkUniformity(vec);
bool b2 = checkUniformity(arr);
Run Code Online (Sandbox Code Playgroud)

最佳和最易读的方法是什么?

也欢迎任何有关代码改进的建议(样式,设计)。谢谢!

Jar*_*d42 6

您要template:

template <typename container_type>
bool checkUniformity(const container_type& container)
{
    for(size_t i = 1; i < container.size(); i++)
    {
        const auto& o1 = container[i-1];
        const auto& o2 = container[i];

        if(!o1.isUniform(o2))
            return false;
    }
    return true;
}
Run Code Online (Sandbox Code Playgroud)

  • 如果不是,则将出现编译时错误。您可能仍然SFINAE会因为错误的类型而从重载中放弃该方法(您也可能会编译时错误,但消息不同)。 (3认同)

Erl*_*nig 6

如果您使用迭代器和范围而不是直接使用容器,则可以生成一种可以有效地与任何容器(包括链接列表)以及流一起使用的算法:

#include <list>
#include <array>
#include <vector>
#include <iterator>

template <typename T>
bool checkUniformity(T begin, T end) {
    // Check for empty range
    if (begin == end) return true;

    // Remember last element
    T last = begin;
    while (++begin != end) {
        if (!((*last).isUniform(*begin)))
            return false;
        last = begin;
    }

    return true;
}

template <typename T, typename F>
bool checkUniformity(T begin, T end, F pred) {
    // Check for empty range
    if (begin == end) return true;

    // Remember last element
    T last = begin;
    while (++begin != end) {
        if (!pred(*last, *begin))
            return false;
        last = begin;
    }

    return true;
}

struct Foo
{
    bool isUniform(const Foo& other) const;
};


int main () {
    // I want to call it in both ways:
    std::vector<Foo> vec;
    std::array<Foo, 3> arr;
    std::list<Foo> list;
    Foo carr [3];

    bool b1 = checkUniformity(std::cbegin(vec), std::cend(vec));
    bool b2 = checkUniformity(std::cbegin(arr), std::cend(arr));
    bool b3 = checkUniformity(std::cbegin(list), std::cend(list));
    bool b4 = checkUniformity(std::cbegin(carr), std::cend(carr));

    bool b1_2 = checkUniformity(std::cbegin(vec), std::cend(vec), [] (const Foo& a, const Foo& b) { return a.isUniform(b); });
    bool b2_2 = checkUniformity(std::cbegin(arr), std::cend(arr), [] (const Foo& a, const Foo& b) { return a.isUniform(b); });
    bool b3_2 = checkUniformity(std::cbegin(list), std::cend(list), [] (const Foo& a, const Foo& b) { return a.isUniform(b); });
    bool b4_2 = checkUniformity(std::cbegin(carr), std::cend(carr), [] (const Foo& a, const Foo& b) { return a.isUniform(b); });
}
Run Code Online (Sandbox Code Playgroud)

您还可以实现如图所示的第二种变体,其中您可以将条件指定为谓词(例如 lambda,如图所示),以防您有不同的 变体isUniform。必须传递范围的两个参数而不仅仅是容器稍微麻烦一些,但更灵活;它还允许您在容器的子范围上运行算法。

这与标准库算法(例如std::find.


Pet*_*and 5

接受几乎任何容器类型是使用带有模板模板参数的模板的好主意。C++ 中的大多数容器都接受它们持有的值类型和用于分配内存的分配器类型作为第一个模板参数。

要检查容器的值类型是否实现了某种方法,isUniform()在这种情况下,您可以使用std::enable_if.

#include <iostream>
#include <vector>
#include <type_traits>

struct Foo
{
    bool isUniform(const Foo&) const { return true; }
};

//Template template parameter TContainer, that accepts 2 template parameters
//the first for the value_type, the second for the allocator type
template <template <typename, typename> typename TContainer, typename TValue, typename TAllocator>
auto checkUniformity(TContainer<TValue, TAllocator>& container)
//Using `std::enable_if` to check if the result of invoking the `isUniform()` method is bool
//in case it is not bool, or the method does not exist, the `std::enable_if_t` will result
//in an error
-> std::enable_if_t
<
    std::is_same_v
    <
        decltype(std::declval<TValue>().isUniform(std::declval<TValue>())), 
        bool
    >, 
    bool
>
{
    for(size_t i = 1; i < container.size(); i++)
    {
        const auto& o1 = container[i-1];
        const auto& o2 = container[i];

        if(!o1.isUniform(o2))
            return false;
    }

    return true;
}

int main()
{
    std::vector<Foo> vec(10);
    std::cout << std::boolalpha << checkUniformity(vec);

    return 0;
}
Run Code Online (Sandbox Code Playgroud)

请注意,std::array它没有分配器类型,因此此方法不适用于std::array. 为此,您可以更改TContainer为一个简单的模板类型参数,并typename TContainer::value_type在您会使用的任何地方使用TValue.