使用std :: transform为自定义类型实现标量和向量加法 [英] Implementing Scalar and Vector Addition for Custom Type with 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:
- 对于
std :: size_t
的#include
的正确标头不是stdlib.h
,但
cstddef
。 (一般来说,不要#include
*。h
,而c * code>版本。)
- 您可能想要添加一个默认构造函数,以使用空向量初始化您的类型。
- 使构造函数采用一个argumrnt
显式
。当整数突然转换为my_data
时,它避免了不好的惊喜。 - 接受
std的构造函数: :vector< T>
不必要地复制其参数。至少,你应该std :: move
它到它的目的地。你可能还想考虑为const std :: vector< T>&
(它做一个副本)和std :: vector<
- 在构造函数中优先分配初始化列表。
- 使用C ++ 11 lambdas代替旧粘合剂。
- 考虑重载操作符
+ =
和<< / code>而不是提供命名成员函数。
- 您的向量添加需要一个大小检查。
std :: transform
无法判断第二个和第三个范围是多少。 - 添加函数不应该产生不必要的副本它们的参数。
- 在向量加法的
std :: transform
调用中,应提供一个二进制函数。由于您已经对该函数进行模板化,因此std :: plus< T>
或std :: plus< U>
适用。使用添加了T
和U
的lambda。如果例如T = std :: string
和U = char *
,这也将更有效。 li>
通常喜欢C ++ 11的统一初始化语法(
{...}
)。 - The correct header to
#include
forstd::size_t
is notstdlib.h
butcstddef
. (Generally, don't#include
the*.h
but thec*
versions.) - You probably want to add a default constructor that initializes your type with an empty vector.
- You probably want to make the constructors that take one argumrnt
explicit
. It avoids bad surprises when an integer suddenly converts to amy_data
. - The constructor that takes a
std::vector<T>
needlessly makes a copy of its argument. At the very least, you shouldstd::move
it into its destination. You might also want to consider providing an overload forconst std::vector<T>&
(which does a copy) andstd::vector<T>&&
(which does a move). - Prefer initializer lists over assignment in the constructor.
- Generally prefer C++11's uniform initialization syntax (
{ … }
). - Use C++11 lambdas instead of the legacy binders. It's more readable and at least as efficient.
- Consider overloading operators
+=
and<<
instead of providing named member functions. - Your vector addition needs a size-check.
std::transform
cannot tell how long the second and third ranges are. - The addition functions shouldn't make a needless copy of their argument.
- In the vector addition's
std::transform
call, a binary function should be provided. Since you have templated the function, neitherstd::plus<T>
norstd::plus<U>
are applicable. Use a lambda that adds aT
and aU
. This will also be more efficient if, for exampleT = std::string
andU = 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屋!