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

时间:2018-01-17 10:39:51

标签: c++ templates std c++17

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

我有两个问题:

  1. 为什么std::sizestd::empty constexpr?据我所知,只有std::array的大小和空虚才能在编译时知道堆栈上的数组,而std::vector和{{1}等其他容器则不然。 }。那么,当std::map替换为std::size作为模板参数时,std::emptystd::vector如何工作?
  2. 我尝试使用stdstd::size的模板特化扩展std::empty用于我的自定义容器,但编译器无法推断出专门的模板方法。有人可以解释我做错了吗?
  3. 我的代码 [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; }
    

3 个答案:

答案 0 :(得分:2)

  
      
  1. 我尝试使用std :: size和std :: empty的模板特化为我的自定义容器扩展std,但是编译器无法推断出专门的模板方法。有人可以解释我做错了吗?
  2.   

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。

所以你的功能不是专业化。

因此,除非您在班级中添加widget::size()声明,否则您无法在std中专门化该功能。

答案 1 :(得分:1)

  

为什么std::sizestd::empty constexpr

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

  

我尝试使用std::sizestd::empty的模板特化为我的自定义容器扩展std,但编译器无法推断出专门的模板方法。

这是因为您尝试提供的std::sizestd::empty版本具有不同的返回类型。标准返回类型为decltype(c.size()) / decltype(c.empty())。您没有提供size()empty()成员函数,因此这不可行。只需使用标准名称。

答案 2 :(得分:1)

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

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

然后

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

工程;并且用{C}或your_container容器替换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