【问题标题】:Can non type template arguments done on stl containers?可以在 stl 容器上完成非类型模板参数吗?
【发布时间】:2019-09-23 09:37:56
【问题描述】:
template<typename T,int nSize>
T Sum(T (&parr)[nSize])
{
    T sum=0;
    for(int i = 0; i < nSize ;++i)
    {
        sum += parr[i];
    }
    return sum;
}
int _tmain(int argc, _TCHAR* argv[])
{
    int nArr[] = {1,2,3,4};
    int nSum = Sum(nArr);
   std::cout<<"Sum :"<<nSum;
}

可以使用std::vector 代替array。或者array 可以用任何stl 容器代替吗?

【问题讨论】:

  • 没有。采用两个模板化迭代器而不是一个数组,您的函数几乎可以与任何容器一起使用。
  • 如何使用两个模板化迭代器来完成。您能详细说明一下吗?
  • @MohammedAliJahfer,为你的函数使用一个范围。 T 类型的一个模板参数和输入迭代器的另一个模板参数
  • @kuro 所以我们不能使用非类型模板参数来完成。对吧?

标签: c++ c++11 templates containers c++-standard-library


【解决方案1】:

可以使用std::vector 代替array。或者可以将array 替换为 任何 stl 容器?

没有。这是不可能的,因为它们的类型不同。但是您可以通过以下方式概括给定函数。

制作一个模板函数,获取容器的开始和结束迭代器。然后使用std::accumulate,将元素相加,这将适用于any sequence containers 以及数组:

以下是示例代码:(See live online)

#include <iostream>
#include <string>
#include <vector>
#include <list>
#include <numeric>  // std::accumulate
#include <iterator> // std::iterator_traits, std::cbegin, std::cend

template<typename Iterator>
constexpr auto Sum(Iterator begin, const Iterator end) -> typename std::iterator_traits<Iterator>::value_type
{
    using RetType = typename std::iterator_traits<Iterator>::value_type;
    return std::accumulate(begin, end, RetType{});
}

int main()
{
    int nArr[] = { 1,2,3,4 };
    std::vector<int> vec{ 1,2,3,4 };
    std::list<int> list{ 1,2,3,4 };
    // now you can
    std::cout << "Sum of array: " << Sum(std::cbegin(nArr), std::cend(nArr)) << "\n";
    std::cout << "Sum of vec: "   << Sum(std::cbegin(vec), std::cend(vec)) << "\n";
    std::cout << "Sum of list: "  << Sum(std::cbegin(list), std::cend(list)) << "\n";
}

输出:

Sum of array: 10
Sum of vec: 10
Sum of list: 10

【讨论】:

    【解决方案2】:
    template<typename T, int nSize>
    T sum(std::array<T, nSize> const&);
    

    将是std::array 的等效签名。如您所见,签名已经不同。尝试对std::vector 做同样的事情肯定会失败:

    template<typename T, int nSize>
    T sum(std::vector<T> const&);
    

    你怎么知道在编译时有多少元素将驻留在向量中???你根本做不到。即使您在代码中明确指定了nSize,例如。 G。 sum&lt;std::vector&lt;int&gt;, 7&gt;,然后该函数将总是尝试迭代恰好七个元素,如果有更少的元素会导致未定义的行为,如果有更多的元素则不计算剩余的行为......

    典型的方法是使用开始和结束迭代器,就像标准库对所有算法一样:

    template <typename Iterator>
    auto sum(Iterator begin, Iterator end) -> std::remove_reference_t<decltype(*begin)>
    {
        using type = decltype(sum(begin, end)); // just not wanting to repeat all
                                                // that remove_reference stuff...
        type s = type();
        for( ; begin != end; ++begin)
        {
            s += *begin;
        }
        return s;
    }
    

    您还可以基于此函数为任意容器提供通用重载:

    template <typename Container>
    auto sum(Container const& c)
    {
        using std::begin;
        using std::end;
        return sum(begin(c), end(c));
    }
    

    【讨论】:

      【解决方案3】:

      如果您的编译器支持 C++ 17,那么您可以使用 if constexpr 语句编写单个函数。

      例如

      #include <iostream>
      #include <vector>
      
      template<typename T>
      auto Sum( const T &container )
      {
          if constexpr( std::is_array_v<std::remove_reference_t<T>> )
          {
              std::remove_extent_t<T> sum = 0;
              for ( const auto &item : container )
              {
                  sum += item;
              }
      
              return sum;
          }
          else
          {
              typename T::value_type sum = 0;
              for ( const auto &item : container )
              {
                  sum += item;
              }
      
              return sum;
          }
      }
      
      int main()
      {
          int nArr[] = { 1, 2, 3, 4 };
          int nSum = Sum( nArr );
          std::cout << "Sum :"<<nSum << '\n';;
      
          std::vector<int> v = { 1, 2, 3, 4 };
          nSum = Sum( v );
          std::cout << "Sum :"<<nSum << '\n';;
      }
      

      程序输出是

      Sum :10
      Sum :10
      

      不过,最好将函数拆分为两个函数:一个用于数组,另一个用于标准容器。

      #include <iostream>
      #include <vector>
      
      template<typename T, size_t N>
      auto Sum( const T ( &a )[N] )
      {
          T sum = 0;
      
          for ( const auto &item : a )
          {
              sum += item;
          }
      
          return sum;
      }
      
      template<typename T>
      auto Sum( const T &container )
      {
          typename T::value_type sum = 0;
          for ( const auto &item : container )
          {
              sum += item;
          }
      
          return sum;
      }
      
      int main()
      {
          int nArr[] = { 1, 2, 3, 4 };
          int nSum = Sum( nArr );
          std::cout << "Sum :"<<nSum << '\n';;
      
          std::vector<int> v = { 1, 2, 3, 4 };
          nSum = Sum( v );
          std::cout << "Sum :"<<nSum << '\n';;
      }
      

      【讨论】:

      • 对于通用代码,可能类似于 std::decay_t&lt;decltype(*std::begin(container))&gt; Sum{};。
      • @Aconcagua 对于迭代器,有标准算法 std::accumulate。您可以在模板函数而不是循环中调用它。
      • 哈哈,我的评论太慢了...实际上,我认为计算总和只是作为创建 mre 的示例,而真实代码可能会做一些完全不同的事情——解释太多?
      猜你喜欢
      • 1970-01-01
      • 2019-09-23
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2020-10-01
      • 1970-01-01
      • 1970-01-01
      • 2012-10-25
      相关资源
      最近更新 更多