使用std :: transform为自定义类型实现标量和向量加法

时间:2015-02-26 14:54:37

标签: templates c++11 overloading typename

这对我来说就像一个基本问题,所以请提前为重复的帖子道歉。不知道术语是什么。

我有一个班级,比如my_data,用于存储数字。它具有基本的构造函数等,并且支持基本操作(例如添加元素,例如元素)将是很方便的。增加数据中的值。

这是描述课程的最基本方式:

#include <stdlib.h>
#include <vector>
#include <iostream>     
#include <algorithm>     
#include <numeric>      
#include <iterator>     

template <typename T>

class my_data {

public:
  my_data  (const my_data& other);
  my_data& operator=(const my_data& rhs);
  my_data  (const size_t bsize) { my_datavec.resize(bsize); }
  my_data  (std::vector<T> init) { my_datavec = init; }  

  template <typename U>
  void add(const U addition) {
    std::transform(my_datavec.begin(), my_datavec.end(), 
                   my_datavec.begin(), 
                   bind2nd(std::plus<T>(), addition) );
  }

  template <typename U>
  void add(const my_data<U> addition) {
     std::transform(my_datavec.begin(), my_datavec.end(), 
                    addition.my_datavec.begin(),
                    my_datavec.begin(), 
                    std::plus<T>() );
  }  

  void print() {    
    std::ostream_iterator<T> out_it (std::cout,", ");
    std::copy(my_datavec.begin(), my_datavec.end(), out_it);
  }

protected:
  std::vector<T> my_datavec;

};

int main() {

  std::vector<int> int_test({1,2,3});
  my_data<int> a(int_test);

  a.add(2);
  a.print();

  a.add(a);
  a.print();

  return(0);

}

当我想将一个对象的值添加到另一个对象的值时,问题就开始了(在这里滥用这个事实,它们的大小相同,省略了检查):

$ g++ -std=c++11 template2.cpp -o template2
In file included from /usr/include/c++/4.7/bits/stl_function.h:741:0,
             from /usr/include/c++/4.7/string:50,
             from /usr/include/c++/4.7/bits/locale_classes.h:42,
             from /usr/include/c++/4.7/bits/ios_base.h:43,
             from /usr/include/c++/4.7/ios:43,
             from /usr/include/c++/4.7/ostream:40,
             from /usr/include/c++/4.7/iostream:40,
             from template2.cpp:3:
/usr/include/c++/4.7/backward/binders.h: In instantiation of ‘std::binder2nd<_Operation> std::bind2nd(const _Operation&, const _Tp&) [with _Operation = std::plus<int>; _Tp = my_data<int>]’:
template2.cpp:20:5:   required from ‘void my_data<T>::add(U) [with U = my_data<int>; T = int]’
template2.cpp:45:10:   required from here
/usr/include/c++/4.7/backward/binders.h:170:57: error: invalid cast from type ‘const my_data<int>’ to type ‘_Arg2_type {aka int}’

似乎没有使用将两个my_data个对象添加到一起的代码。当然<typename U>可以是X类型的对象以及my_data<X>但是如何让编译器知道何时使用第二版add()

我当前的程序版本使用

void add (const bisArray<U> addition, const std::true_type&) 

用于添加元素和

void add (const bisArray<U> addition, const std::false_type&) 

用于添加my_data对象(调用a.add(2, std::is_arithmetic<int>::type() );用于添加元素,a.add(a, std::is_arithmetic<my_data<int>>::type() );用于添加my_data对象。

这不是一个真正的解决方案,因为没有什么可以阻止调用a.add( a , std::is_arithmetic<int>::type() );a.add( 2 , std::is_arithmetic<my_data<int>>::type() );的出现导致分段错误。

有没有一种机制可以更优雅地解决这个问题?

1 个答案:

答案 0 :(得分:1)

您的代码存在许多问题。 {/ 3}}已在评论中指出了代码未编译的最直接原因。

我将从“声明顺序”开始,这不是严重性的顺序:

  1. #include std::size_t的正确标头不是stdlib.h,而是cstddef。 (一般情况下,不要#include *.h而是c*版本。)
  2. 您可能希望添加一个默认构造函数,使用空向量初始化您的类型。
  3. 你可能想要构造一个argumrnt explicit的构造函数。当整数突然转换为my_data时,它可以避免意外情况。
  4. 带有std::vector<T>的构造函数不必要地复制其参数。至少,你应std::move进入目的地。您可能还需要考虑为const std::vector<T>&(执行副本)和std::vector<T>&&(执行此操作)提供重载。
  5. 首选构造函数中的初始化程序列表。
  6. 通常更喜欢C ++ 11的统一初始化语法({ … })。
  7. 使用C ++ 11 lambdas代替遗留绑定器。它更具可读性,至少效率更高。
  8. 考虑重载运算符+=<<,而不是提供命名成员函数。
  9. 您的矢量添加需要进行尺寸检查。 std::transform无法判断第二和第三范围有多长。
  10. 添加功能不应该是他们的论点的不必要的副本。
  11. 在向量加法std::transform调用中,应提供二进制函数。由于您已模板化该功能,因此std::plus<T>std::plus<U>均不适用。使用添加TU的lambda。如果例如T = std::stringU = char *
  12. ,这也会更有效

    让我们把所有这些放在一起:

    #include <algorithm>
    #include <cstddef>
    #include <iostream>
    #include <iterator>
    #include <stdexcept>
    #include <vector>
    
    template<typename T>
    class my_data
    {
    
    public:
    
      my_data()
      {
      }
    
      explicit my_data (const std::size_t size)
      {
        data.resize(size);
      }
    
      explicit my_data(const std::vector<T>& init) : data {init}
      {
      }
    
      explicit my_data(std::vector<T>&& init) : data {std::move(init)}
      {
      }
    
      template<typename U>
      void
      operator+=(const U& a)
      {
        std::transform(data.begin(),
                       data.end(),
                       data.begin(),
                       [&a](const T& b){ return a + b; });
      }
    
      template<typename U>
      void
      operator+=(const my_data<U>& other)
      {
        if (other.data.size() != this->data.size())
          throw std::invalid_argument {"incompatible sizes"};
        std::transform(data.begin(),
                       data.end(),
                       other.data.begin(),
                       data.begin(),
                       [](const T& a, const U& b){ return a + b; });
      }
    
      friend
      std::ostream&
      operator<<(std::ostream& os, const my_data& md)
      {
        std::ostream_iterator<T> outit {os, ", "};
        std::copy(md.data.begin(), md.data.end(), outit);
        return os;
      }
    
    protected:
    
      std::vector<T> data {};
    };
    
    int main() {
      std::vector<int> inttest {1, 2, 3};
      my_data<int> a {inttest};
      std::cout << a << std::endl;
      a += 2;
      std::cout << a << std::endl;
      a += a;
      std::cout << a << std::endl;
    }
    

    输出:

    1, 2, 3, 
    3, 4, 5, 
    6, 8, 10, 
    

    您可能希望解决的另一件事是输出中多余的尾随逗号。