std::size 和 std::empty 的特化与模板不匹配

Specializations of std::size and std::empty do not match the template

我想通过为我的自定义容器(在 std 之外)添加 std::sizestd::empty 的模板特化来扩展 std 命名空间。

我有两个问题:

  1. 为什么是std::size and std::emptyconstexpr?据我所知,在编译时只能知道 std::array 和堆栈上的数组的大小和空性,而对于其他容器,例如 std::vectorstd::map.那么 std::sizestd::empty 甚至在将 std::vector 替换为模板参数时如何工作呢?
  2. 我尝试使用自定义容器的 std::sizestd::empty 模板特化扩展 std,但编译器无法推断出专用模板方法。有人可以解释我做错了什么吗?

我的代码 [Try it online in Wandbox]:

#include <iterator>
#include <vector>

namespace dummy {
    struct Widget {
        bool IsEmpty() const noexcept { return m_v.empty(); }
        size_t GetSize() const noexcept { return m_v.size(); }
        std::vector< int > m_v;
    };
}

namespace std {

    template<>
    constexpr auto empty(const dummy::Widget &widget)
        -> decltype(widget.IsEmpty()) {

        return widget.IsEmpty();
    }

    template<>
    constexpr auto size(const dummy::Widget &widget)
        -> decltype(widget.GetSize()) {

        return widget.GetSize();
    }
}

int main() {
    std::vector< int > ints;
    std::size(ints);
}

输出:

prog.cc:15:17: error: no function template matches function template specialization 'empty'
        constexpr auto empty(const dummy::Widget &widget)
                       ^
/opt/wandbox/clang-head/include/c++/v1/iterator:1843:16: note: candidate template ignored: substitution failure [with _Cont = dummy::Widget]: no member named 'empty' in 'dummy::Widget'
constexpr auto empty(const _Cont& __c)
               ^
/opt/wandbox/clang-head/include/c++/v1/iterator:1850:16: note: candidate template ignored: could not match 'type-parameter-0-0 const[_Np]' against 'const dummy::Widget'
constexpr bool empty(const _Tp (&)[_Sz]) noexcept { return false; }
               ^
/opt/wandbox/clang-head/include/c++/v1/iterator:1854:16: note: candidate template ignored: could not match 'initializer_list<type-parameter-0-0>' against 'const dummy::Widget &'
constexpr bool empty(initializer_list<_Ep> __il) noexcept { return __il.size() == 0; }
               ^
prog.cc:22:17: error: no function template matches function template specialization 'size'
        constexpr auto size(const dummy::Widget &widget)
                       ^
/opt/wandbox/clang-head/include/c++/v1/iterator:1832:16: note: candidate template ignored: substitution failure [with _Cont = dummy::Widget]: no member named 'size' in 'dummy::Widget'
constexpr auto size(const _Cont& __c)
               ^
/opt/wandbox/clang-head/include/c++/v1/iterator:1839:18: note: candidate template ignored: could not match 'type-parameter-0-0 const[_Np]' against 'const dummy::Widget'
constexpr size_t size(const _Tp (&)[_Sz]) noexcept { return _Sz; }
  1. I tried to extend the std with template specializations of std::size and std::empty for my custom containers, but the compiler could not deduce the specialized template method. Could someone explain what I do wrong?

std::size签名为:

template <class C>
constexpr auto size( const C& c ) -> decltype(c.size());

你的不一样:

template<>
constexpr auto size(const dummy::Widget &widget)
    -> decltype(widget.GetSize());

decltype(..)内容不一样,你做SFINAE的方法不一样

所以你的函数不是专业化。

因此,除非您在 class 中添加 widget::size() 声明,否则您无法在 std.

中特化该函数

Why are std::size and std::empty constexpr?

对于函数模板,constexpr 仅表示如果可能,结果函数将是 constexpr。这并不意味着所有实例化都是 constexpr 。在 std::vector 的情况下,这意味着 std::size 的特定实例不会。

I tried to extend the std with template specializations of std::size and std::empty for my custom containers, but the compiler could not deduce the specialized template method.

那是因为您尝试为其提供专业化的 std::sizestd::empty 版本具有不同的 return 类型。标准的 return 类型是 decltype(c.size()) / decltype(c.empty())。您不提供 size()empty() 成员函数,因此这不可能工作。只需使用标准名称即可。

扩展 std::size 的正确方法不是通过专业化。

相反,您应该在与您的类型相同的命名空间中定义一个自由函数 size(可选择作为内联 friend)。

然后

using std::size;
std::cout << size( your_container ) << "\n";

有效; your_container 替换为 C 数组或 std 容器也可以(在通用代码中)。

using std::size 的要求很烦人。您可以解决它:

namespace notstd {
  namespace adl_size {
    using std::size;
    template<class T>
    constexpr auto get_size(T const& t)
    noexcept( noexcept( size(t) ) )
    ->decltype( size(t) )
    { return size(t); }
  }
  template<class T>
  constexpr auto size( T const& t )
  noexcept( noexcept( ::notstd::adl_size::get_size(t) ) )
  -> decltype( ::notstd::adl_size::get_size(t) )
  { return ::notstd::adl_size::get_size(t); }
}

现在 notstd::size( vector )notstd::size( your_container )notstd::size( some_array ) 都可以使用,并且不需要在使用前显式添加 using std::size