使用std :: transform为自定义类型实现标量和向量加法 [英] Implementing Scalar and Vector Addition for Custom Type with std::transform

查看:264
本文介绍了使用std :: transform为自定义类型实现标量和向量加法的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

这看起来像一个基本的问题,所以事先为重复的帖子。

This looks to me like a basic problem so apologies in advance for duplicate posts. Not sure what the terminology is though.

我有一个类,例如 my_data ,用于存储数字。它具有基本的构造函数等,并且它将方便地支持基本操作,例如添加到元素,例如。

I have a class, say my_data, for storing numbers. It has the basic constructors etc. and it would be handy to support elementary operations such as adding to the elements, e.g. increasing the values in the data.

这是描述类的最基本的方式:

This is the most elementary way to describe the class:

#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);

}

问题从我要添加一个值对象到另一个的值(滥用这里的事实,他们是相同的大小,省略检查):

The problem starts when I want to add the values of one object to the values of another (abusing the fact here that they are the same size to omit checks):

$ 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}’

code> my_data 对象似乎没有使用。当然< typename U> 可以是 X 以及 my_data< X> 但是如何让编译器知道何时使用 add()

The code of adding two my_data objects together does not seem to be used. Of course <typename U> can be objects of type X as well as my_data<X> but how do I let the compiler know when to use the second version of 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> code>添加 my_data 对象)。

for adding a my_data object (the calls are a.add(2, std::is_arithmetic<int>::type() ); for adding an element and a.add(a, std::is_arithmetic<my_data<int>>::type() ); for adding a my_data object).

这不是真正的解决方案,因为没有什么可以防止调用 a.add(a,std :: is_arithmetic< int> :: type()); a.add(2,std :: is_arithmetic< my_data< int> :: type());

It is not really a solution though because there is nothing to prevent the occurrence of the calls a.add( a , std::is_arithmetic<int>::type() ); and a.add( 2 , std::is_arithmetic<my_data<int>>::type() ); which lead to a segmentation fault.

推荐答案

有没有机制可以更优雅地解决这个问题? >解决方案

解决方案

您的代码有很多问题。您的代码无法编译的最直接原因是 Igor Tandetnik 在评论中指出。

There are a number of issues with your code. The most immediate reason for your code not compiling was already pointed out by Igor Tandetnik in the comments.

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

I'll start in "declaration order" which is not the order of severity:


  1. 对于 std :: size_t #include 的正确标头不是 stdlib.h ,但 cstddef 。 (一般来说,不要 #include *。h ,而 c * code>版本。)

  2. 您可能想要添加一个默认构造函数,以使用空向量初始化您的类型。

  3. 使构造函数采用一个argumrnt 显式。当整数突然转换为 my_data 时,它避免了不好的惊喜。

  4. 接受 std的构造函数: :vector< T> 不必要地复制其参数。至少,你应该 std :: move 它到它的目的地。你可能还想考虑为 const std :: vector< T>& (它做一个副本)和 std :: vector<

  5. 在构造函数中优先分配初始化列表。

  6. 通常喜欢C ++ 11的统一初始化语法( {...} )。
  7. 使用C ++ 11 lambdas代替旧粘合剂。

  8. 考虑重载操作符 + = << / code>而不是提供命名成员函数。

  9. 您的向量添加需要一个大小检查。 std :: transform 无法判断第二个和第三个范围是多少。

  10. 添加函数不应该产生不必要的副本它们的参数。

  11. 在向量加法的 std :: transform 调用中,应提供一个二进制函数。由于您已经对该函数进行模板化,因此 std :: plus< T> std :: plus< U> 适用。使用添加了 T U 的lambda。如果例如 T = std :: string U = char * ,这也将更有效。 li>
  1. The correct header to #include for std::size_t is not stdlib.h but cstddef. (Generally, don't #include the *.h but the c* versions.)
  2. You probably want to add a default constructor that initializes your type with an empty vector.
  3. You probably want to make the constructors that take one argumrnt explicit. It avoids bad surprises when an integer suddenly converts to a my_data.
  4. The constructor that takes a std::vector<T> needlessly makes a copy of its argument. At the very least, you should std::move it into its destination. You might also want to consider providing an overload for const std::vector<T>& (which does a copy) and std::vector<T>&& (which does a move).
  5. Prefer initializer lists over assignment in the constructor.
  6. Generally prefer C++11's uniform initialization syntax ({ … }).
  7. Use C++11 lambdas instead of the legacy binders. It's more readable and at least as efficient.
  8. Consider overloading operators += and << instead of providing named member functions.
  9. Your vector addition needs a size-check. std::transform cannot tell how long the second and third ranges are.
  10. The addition functions shouldn't make a needless copy of their argument.
  11. In the vector addition's std::transform call, a binary function should be provided. Since you have templated the function, neither std::plus<T> nor std::plus<U> are applicable. Use a lambda that adds a T and a U. This will also be more efficient if, for example T = std::string and U = char *.

让我们把这一切放在一起:

Let's put all this together:

#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, 

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

One more thing that you might wish to address is the superfluous trailing comma in the output.

这篇关于使用std :: transform为自定义类型实现标量和向量加法的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆