如何编写适用于任何类型的集合对象的size()函数?

时间:2018-02-06 16:55:52

标签: c++ templates mfc c++17

我需要一种简单的方法来获取类T对象的计数/长度/大小,其中T是某种集合类型,例如std::map,{{ 1}},std::liststd::vectorCStringArrayCString,...

对于大多数标准类型,std::string是正确的答案,因为大多数MFC类T::size()是正确的,对于T::GetSize(),它是CString。< / p>

我希望有一个:

T::GetLength()

...评估正确的成员函数调用。

似乎应该有一种简单的方法来调用template <typename T> auto size(const T & t) 上具有T成员的特征模板,该成员本身使用SFINAE存在或不存在,如果它存在,那么它根据定义,调用适当的size(const T & t)来返回t.size_function()实例中元素的数量。

我可以编写一个精心设计的T类型 - 特征模板 - 在stackoverflow上有一些例子 - 所有这些例子都让我感到非常困惑&#34;必须有一个更简单的方法&#34;。使用C ++ 17,似乎应该轻松优雅地解决这个问题?

这些讨论herehere似乎使用了一个不优雅的解决方案,其中一些答案使用预处理器宏来完成工作。这仍然是必要的吗?

但是......当然,必须有一种方法可以使用这样一个事实,即在has_member上调用正确的成员函数是可编译的,并且调用错误的成员函数无法编译 - 这不可能是使用直接为给定类型T创建正确的类型特征包装?

我想要的是:

T

选择template <typename T> auto size(const T & collection) { return collection_traits<T>::count(collection); } 的确切特化是因为它是唯一适合collection_traits<T>的特殊化(即它调用正确的实例方法)。

3 个答案:

答案 0 :(得分:31)

您可以使用expression SFINAE和多次重载。

这个想法如下:检查x.size()是否是您的类型的有效表达式 - 如果是,则调用并返回它。重复.getSize.getLength

假设:

struct A { int size() const { return 42; } };
struct B { int getSize() const { return 42; } };
struct C { int GetLength() const { return 42; } };

您可以提供:

template <typename T>
auto size(const T& x) -> decltype(x.size()) { return x.size(); }

template <typename T>
auto size(const T& x) -> decltype(x.getSize()) { return x.getSize(); }

template <typename T>
auto size(const T& x) -> decltype(x.GetLength()) { return x.GetLength(); }

用法:

int main()
{
    size(A{});
    size(B{});
    size(C{});
}

live example on wandbox.org

此解决方案易于扩展,可与使用模板化的容器无缝协作。

  

如果某种类型暴露了两个吸气剂会怎么样?

上述解决方案会导致歧义,但通过引入解决此问题的排名/排序很容易解决。

首先,我们可以创建一个rank类,允许我们任意设置重载的优先级:

template <int N> struct rank : rank<N - 1> { };
template <>      struct rank<0> { };

rank<N>可隐式转换为rank<N - 1>。在重载解析期间,完全匹配优于转换链。

然后我们可以创建size_impl重载的层次结构:

template <typename T>
auto size_impl(const T& x, rank<2>) 
    -> decltype(x.size()) { return x.size(); }

template <typename T>
auto size_impl(const T& x, rank<1>) 
    -> decltype(x.getSize()) { return x.getSize(); }

template <typename T>
auto size_impl(const T& x, rank<0>) 
    -> decltype(x.GetLength()) { return x.GetLength(); }

最后,我们提供了一个接口函数,它开始调度到正确的size_impl重载:

template <typename T>
auto size(const T& x) -> decltype(size_impl(x, rank<2>{})) 
{ 
    return size_impl(x, rank<2>{}); 
}

使用类似D以下的类型

struct D
{
    int size() const { return 42; }
    int getSize() const { return 42; }
    int GetLength() const { return 42; }
};

现在将选择rank<2>的{​​{1}}重载:

live example on wandbox

答案 1 :(得分:9)

最简单的解决方案IMO是函数重载。

// Default implementation for std containers.
template <typename Container>
std::size_t size(Container const& c) { return c.size(); }

// Overloads for others.
std::size_t size(CStringArray const& c) { return c.GetSize(); }
std::size_t size(CString const& c) { return c.GetLength(); }
// ... etc.

答案 2 :(得分:2)

您需要expression SFINAE,并且您必须与可能决定符合这两个界面的其他类型玩得很好,所以请研究std::size()
目标是扩充std::size() 以处理至少遵循其中一个约定的所有类型,只要它们不会试图遵循其中的任何约定。

#include <type_traits>
#include <iterator>

namespace internal {
    // Avoid conflict with std::size()
    template <class C>
    auto size_impl(const C& c, int) -> decltype((void)c.size());

    // Avoid conflict with std::size()
    template <class T, std::size_t N>
    void size_impl(const T (&array)[N], int);

    template <class C>
    constexpr auto size_impl(const C& c, long)
    noexcept(noexcept(c.GetLength()))
    -> decltype(c.GetLength())
    { return c.GetLength(); }

    template <class C>
    constexpr auto size_impl(const C& c, long long)
    noexcept(noexcept(c.getSize()))
    -> decltype(c.getSize())
    { return c.getSize(); }
};

template <class T>
using enable_if_not_void_t = std::enable_if_t<!std::is_void<T>(), T>;

using std::size;
template <class C>
constexpr auto size(const C& c)
noexcept(noexcept(internal::size_impl(c, 0)))
-> enable_if_not_void_t<decltype(internal::size_impl(c, 0))>
{ return internal::size_impl(c, 0); }

使用模板和继承扩展内容可以获得任意级别的优先级:

template <std::size_t N>
struct priority : priority<N - 1> {};
template <>
struct priority<0> {};

类似于提议的Abbreviated Lambdas for Fun and Profit会大大简化事情。