I want to define a set that does its comparison based on the value of other members of the current class:
std::set<ContentType> mySet(doComparison(*this));
where doCompare is a struct:
struct doCompare{
doCompare( MyClass& mc ) : _mc(mc) { }
MyClass& _mc;
bool operator()( const ContentType & i1, const ContentType & i2 ){
return _mc.otherMember[i1] < _mc.otherMemeber[i2];
}
};
Here the mySet is a member of MyClass and when I try to initialize the set with the comparison function in the initialization list: mySet(doCompare(*this)) the code does not compile.
What am I doing wrong here?
The error is:
no matching function for call to ``std::set<ContentType>::set(MyClass::doCompare)`
Here is the full message (with the change of names for better readability):
./myclass.h:74:165: error: no matching function for call to ‘std::set<ContentType>::set(MyClass::doCompare)’
: mySet(doCompare(*this)) {
^
./myclass.h:74:165: note: candidates are:
In file included from /usr/include/c++/4.8/set:61:0,
from ./myclass.h:12,
from [blah blah]
/usr/include/c++/4.8/bits/stl_set.h:193:7: note: std::set<_Key, _Compare, _Alloc>::set(const std::set<_Key, _Compare, _Alloc>&) [with _Key = ContentType; _Compare = std::less<ContentType >; _Alloc = std::allocator<ContentType >]
set(const set& __x)
^
/usr/include/c++/4.8/bits/stl_set.h:193:7: note: no known conversion for argument 1 from ‘MyClass::doCompare’ to ‘const std::set<ContentType >&’
/usr/include/c++/4.8/bits/stl_set.h:180:2: note: template<class _InputIterator> std::set<_Key, _Compare, _Alloc>::set(_InputIterator, _InputIterator, const _Compare&, const allocator_type&)
set(_InputIterator __first, _InputIterator __last,
^
/usr/include/c++/4.8/bits/stl_set.h:180:2: note: template argument deduction/substitution failed:
In file included from [blahblah]:
./myclass.h:74:165: note: candidate expects 4 arguments, 1 provided
: mySet(doCompare(*this)) {
^
In file included from /usr/include/c++/4.8/set:61:0,
from ./myclass.h:12,
/usr/include/c++/4.8/bits/stl_set.h:163:2: note: template<class _InputIterator> std::set<_Key, _Compare, _Alloc>::set(_InputIterator, _InputIterator)
set(_InputIterator __first, _InputIterator __last)
^
/usr/include/c++/4.8/bits/stl_set.h:163:2: note: template argument deduction/substitution failed:
In file included from [blahblah]:
./myclass.h:74:165: note: candidate expects 2 arguments, 1 provided
: mySet(doCompare(*this)) {
^
In file included from /usr/include/c++/4.8/set:61:0,
from ./myclass.h:12,
from blahblah:
/usr/include/c++/4.8/bits/stl_set.h:148:7: note: std::set<_Key, _Compare, _Alloc>::set(const _Compare&, const allocator_type&) [with _Key = ContentType; _Compare = std::less<ContentType >; _Alloc = std::allocator<ContentType >; std::set<_Key, _Compare, _Alloc>::allocator_type = std::allocator<ContentType >]
set(const _Compare& __comp,
^
/usr/include/c++/4.8/bits/stl_set.h:148:7: note: no known conversion for argument 1 from ‘MyClass::doCompare’ to ‘const std::less<ContentType >&’
/usr/include/c++/4.8/bits/stl_set.h:139:7: note: std::set<_Key, _Compare, _Alloc>::set() [with _Key = ContentType; _Compare = std::less<ContentType>; _Alloc = std::allocator<ContentType >]
set()
^
To summarize the issue:
It seems I can't initialize the set with the comparison in its declaration, because it depends on this.
The initialization with comparison function after the set is declared is failing, but I don't know why.
SOLUTION thanks to #WhozCraig
declare mySet as:
std::set<ContentType, doCompare> mySet;
and initialize it in the initialization list as:
mySet(doCompare(*this))
You're not declaring your set type with the proper template comparator parameter. This:
std::set<ContentType> mySet;
means this when expanded:
std::set<ContentType, std::less<ContentType>> mySet
leaving out the allocator for brevity. This means when constructing mySet and specifying an alternate comparator functor, it must be of type std::less<ContentType>, but yours is not. It is of type doCompare. The compiler tries to match against every other constructor argument list, failing to find any match, finally resulting in your error.
Change your declaration of mySet to:
std::set<ContentType,doCompare> mySet;
Now the types should wire up correctly.
And as I said in comments, i see no reason the reference to your MyClass being held in your comparator object should be non-const. unless you can think of a good reason, I suggest changing the reference to const instead, i.e. const MyClass&
Related
There's some code I've written which I have trouble with, but I've tried to write a simplified version of it to possibly reproduce the error I'm encountering:
#include <iostream>
#include<bits/stdc++.h>
using namespace std;
struct test{
float y;
};
typedef pair<int, int> intpair;
map<intpair, test> mp;
void func(test &obj){
obj.y = 23.5;
}
int main () {
test* obj = new test;
auto key = make_pair(1,1);
mp.insert(key, obj);
func(*obj);
//cout << *(obj->y);
delete obj;
return 0;
}
What I would like to do is map a pair to a custom struct test that I have created. I get the following error:
main.cpp: In function ‘int main()’:
main.cpp:22:22: error: no matching function for call to ‘std::map, test>::insert(std::pair&, test*&)’
mp.insert(key, obj);
^
In file included from /usr/include/c++/6/map:61:0,
from /usr/include/x86_64-linux-gnu/c++/6/bits/stdc++.h:81,
from main.cpp:2:
/usr/include/c++/6/bits/stl_map.h:731:7: note: candidate: std::pair, std::_Select1st >, _Compare, typename __gnu_cxx::__alloc_traits<_Allocator>::rebind >::other>::iterator, bool> std::map<_Key, _Tp, _Compare, _Alloc>::insert(const value_type&) [with _Key = std::pair; _Tp = test; _Compare = std::less >; _Alloc = std::allocator, test> >; typename std::_Rb_tree<_Key, std::pair, std::_Select1st >, _Compare, typename __gnu_cxx::__alloc_traits<_Allocator>::rebind >::other>::iterator = std::_Rb_tree_iterator, test> >; std::map<_Key, _Tp, _Compare, _Alloc>::value_type = std::pair, test>]
insert(const value_type& __x)
^~~~~~
/usr/include/c++/6/bits/stl_map.h:731:7: note: candidate expects 1 argument, 2 provided
/usr/include/c++/6/bits/stl_map.h:739:9: note: candidate: template std::pair, std::_Select1st >, _Compare, typename __gnu_cxx::__alloc_traits<_Allocator>::rebind >::other>::iterator, bool> std::map<_Key, _Tp, _Compare, _Alloc>::insert(_Pair&&) [with _Pair = _Pair; = ; _Key = std::pair; _Tp = test; _Compare = std::less >; _Alloc = std::allocator, test> >]
insert(_Pair&& __x)
^~~~~~
/usr/include/c++/6/bits/stl_map.h:739:9: note: template argument deduction/substitution failed:
main.cpp:22:22: note: candidate expects 1 argument, 2 provided
mp.insert(key, obj);
^
In file included from /usr/include/c++/6/map:61:0,
from /usr/include/x86_64-linux-gnu/c++/6/bits/stdc++.h:81,
from main.cpp:2:
/usr/include/c++/6/bits/stl_map.h:752:7: note: candidate: void std::map<_Key, _Tp, _Compare, _Alloc>::insert(std::initializer_list >) [with _Key = std::pair; _Tp = test; _Compare = std::less >; _Alloc = std::allocator, test> >]
insert(std::initializer_list<value_type> __list)
^~~~~~
/usr/include/c++/6/bits/stl_map.h:752:7: note: candidate expects 1 argument, 2 provided
/usr/include/c++/6/bits/stl_map.h:781:7: note: candidate: std::map<_Key, _Tp, _Compare, _Alloc>::iterator std::map<_Key, _Tp, _Compare, _Alloc>::insert(std::map<_Key, _Tp, _Compare, _Alloc>::const_iterator, const value_type&) [with _Key = std::pair; _Tp = test; _Compare = std::less >; _Alloc = std::allocator, test> >; std::map<_Key, _Tp, _Compare, _Alloc>::iterator = std::_Rb_tree_iterator, test> >; std::map<_Key, _Tp, _Compare, _Alloc>::const_iterator = std::_Rb_tree_const_iterator, test> >; std::map<_Key, _Tp, _Compare, _Alloc>::value_type = std::pair, test>]
insert(const_iterator __position, const value_type& __x)
^~~~~~
/usr/include/c++/6/bits/stl_map.h:781:7: note: no known conversion for argument 1 from ‘std::pair’ to ‘std::map, test>::const_iterator {aka std::_Rb_tree_const_iterator, test> >}’
/usr/include/c++/6/bits/stl_map.h:792:9: note: candidate: template std::map<_Key, _Tp, _Compare, _Alloc>::iterator std::map<_Key, _Tp, _Compare, _Alloc>::insert(std::map<_Key, _Tp, _Compare, _Alloc>::const_iterator, _Pair&&) [with _Pair = _Pair; = ; _Key = std::pair; _Tp = test; _Compare = std::less >; _Alloc = std::allocator, test> >]
insert(const_iterator __position, _Pair&& __x)
^~~~~~
/usr/include/c++/6/bits/stl_map.h:792:9: note: template argument deduction/substitution failed:
main.cpp:22:22: note: cannot convert ‘key’ (type ‘std::pair’) to type ‘std::map, test>::const_iterator {aka std::_Rb_tree_const_iterator, test> >}’
mp.insert(key, obj);
^
In file included from /usr/include/c++/6/map:61:0,
from /usr/include/x86_64-linux-gnu/c++/6/bits/stdc++.h:81,
from main.cpp:2:
/usr/include/c++/6/bits/stl_map.h:807:9: note: candidate: template void std::map<_Key, _Tp, _Compare, _Alloc>::insert(_InputIterator, _InputIterator) [with _InputIterator = _InputIterator; _Key = std::pair; _Tp = test; _Compare = std::less >; _Alloc = std::allocator, test> >]
insert(_InputIterator __first, _InputIterator __last)
^~~~~~
/usr/include/c++/6/bits/stl_map.h:807:9: note: template argument deduction/substitution failed:
main.cpp:22:22: note: deduced conflicting types for parameter ‘_InputIterator’ (‘std::pair’ and ‘test*’)
mp.insert(key, obj);
Also, I'm sure that I'm going wrong in the way I access my obj's member y (cout statement commented out); How do I go about resolving these issues?
There are no overload of std::map::insert that takes a key and a value.
You can use operator[] to add a new element:
mp[key] = *obj;
Or std::map::insert_or_assign since C++17:
mp.insert_or_assign(key, *obj);
Another point is that type of obj is test* while the type of values of the map mp has type test. The pointer should be dereferenced (like my examples above) or the type of values should be changed to pointers to do this insertion.
I am trying to define a map from member function pointers to strings in C++. But the the compiler complains that operator < is not defined for member function pointers. Is there a way to do this?
Example code:
#include <map>
#include <string>
class foo;
typedef void (foo::*fooFunc)();
class foo {
public:
foo() {
myMap[func]="example function";
}
void func() {};
std::map<fooFunc,std::string> myMap;
};
Resulting error message:
In file included from C:/msys64/mingw64/include/c++/6.2.0/bits/stl_tree.h:65:0,
from C:/msys64/mingw64/include/c++/6.2.0/map:60,
from grr.cpp:1:
C:/msys64/mingw64/include/c++/6.2.0/bits/stl_function.h: In instantiation of 'constexpr bool std::less<_Tp>::operator()(const _Tp&, const _Tp&) const [with _Tp = void (foo::*)()]':
C:/msys64/mingw64/include/c++/6.2.0/bits/stl_map.h:501:32: required from 'std::map<_Key, _Tp, _Compare, _Alloc>::mapped_type& std::map<_Key, _Tp, _Compare, _Alloc>::operator[](std::map<_Key, _Tp, _Compare, _Alloc>::key_type&&) [with _Key = void (foo::*)(); _Tp = std::__cxx11::basic_string<char>; _Compare = std::less<void (foo::*)()>; _Alloc = std::allocator<std::pair<void (foo::* const)(), std::__cxx11::basic_string<char> > >; std::map<_Key, _Tp, _Compare, _Alloc>::mapped_type = std::__cxx11::basic_string<char>; std::map<_Key, _Tp, _Compare, _Alloc>::key_type = void (foo::*)()]'
grr.cpp:9:15: required from here
C:/msys64/mingw64/include/c++/6.2.0/bits/stl_function.h:386:20: error: invalid operands of types 'void (foo::* const)()' and 'void (foo::* const)()' to binary 'operator<'
{ return __x < __y; }
~~~~^~~~~
std::map requires a comparison operator for its keys. By default, it uses operator <. Since operator < is not defined for pointer-to-member types, you are getting this error.
To fix it you'll need to define a custom comparison operator and provide it as a parameter to std::map. Alternatively, use a different container.
From cppreference:
std::map is a sorted associative container that contains key-value
pairs with unique keys. Keys are sorted by using the comparison
function Compare. Search, removal, and insertion operations have
logarithmic complexity. Maps are usually implemented as red-black
trees
If you want options on how to write such a comparison operator, it has already been answered here.
I have a map with the struct defined as under:
struct kv_string {
std::string value;
long long exp_time;
kv_string(const std::string& v): value(v), exp_time(-1) {}
};
Now when I'm trying to insert a new structure using
else if(qargs[0] == "set"){
if(qargs.size()==3){
kv_map.insert(std::make_pair( qargs[1], kv_string(qargs[2])));
}
}
(qargs is a vector<string>), I get the following error:
> In file included from /usr/include/c++/4.8/map:61:0,
> from structures.h:5:
> /usr/include/c++/4.8/bits/stl_map.h: In instantiation of ‘std::map<_Key, _Tp, _Compare, _Alloc>::mapped_type& std::map<_Key,
> _Tp, _Compare, _Alloc>::operator[](const key_type&) [with _Key = std::basic_string<char>; _Tp = kv_string; _Compare =
> std::less<std::basic_string<char> >; _Alloc =
> std::allocator<std::pair<const std::basic_string<char>, kv_string> >;
> std::map<_Key, _Tp, _Compare, _Alloc>::mapped_type = kv_string;
> std::map<_Key, _Tp, _Compare, _Alloc>::key_type =
> std::basic_string<char>]’:
> /usr/include/c++/4.8/stdexcept:281:48: required from here
> /usr/include/c++/4.8/bits/stl_map.h:469:59: error: no matching function for call to ‘kv_string::kv_string()’
> __i = insert(__i, value_type(__k, mapped_type()));
> ^
> /usr/include/c++/4.8/bits/stl_map.h:469:59: note: candidates are:
> structures.h:11:9: note: kv_string::kv_string(const string&)
> kv_string(const std::string& v): value(v), exp_time(-1) {}
> ^
> structures.h:11:9: note: candidate expects 1 argument, 0 provided
> structures.h:8:8: note: kv_string::kv_string(const kv_string&)
> struct kv_string {
> ^
> structures.h:8:8: note: candidate expects 1 argument, 0 provided
> make: *** [server_main.o] Error 1
I have also tried adding an additional constructor kv_string(){}, but it gives a segmentation fault.
You want this:
kv_map.insert(std::make_pair(qargs[1], kv_string(qargs[2]));
Or this:
kv_map.emplace(qargs[1], kv_string(qargs[2]);
Or, in C++17:
kv_map.try_emplace(qargs[1], qargs[2]);
The []-operator default-initializes a new element (if one doesn't exist for the given key), but your type kv_string is not default-constructible. So you cannot use that operator. The above operations are more powerful than the []-operator, too: they return an iterator to the element at the key, and information about whether the key already existed.
The C++ compiler emitted this error message complaining about the lack of a default constructor for your kv_string class:
/usr/include/c++/4.8/bits/stl_map.h:469:59: error: no matching function for call to ‘kv_string::kv_string()’
__i = insert(__i, value_type(__k, mapped_type()));
If you read the documentation for std::map::operator[], you'll see that the mapped type (in your case kv_string) must be default-constructible.
So, if it makes sense for your own design, you could just add a default constructor to your kv_string struct:
struct kv_string {
// ...
// Default constructor
kv_string() : exp_time(-1 /* or whatever default value */) {}
};
As a side note, I would also mark your kv_string(const std::string&) constructor as explicit, to avoid implicit conversions from strings.
I've been looking around for a way to do this, and I'm not sure it's even possible. I've got a class in Java that takes an instance of a generically-typed interface as part of its constructor, and I'd like to recreate it in C++ (it's a utility class that is handy in many situations). To the best of my understanding, the closest equivalent to an interface in C++ is a pure virtual class, and the (somewhat) equivalent of generics is templates.
So let's say I have some classes defined as follows:
template<typename R>
class AnInterface
{
public:
virtual R run() = 0;
virtual ~AnInterface() {}
};
template<typename R>
class Processor
{
public:
Processor(std::vector<AnInterface<R>> toRun) : toRun(toRun) {}
std::vector<R> process() {
std::vector<R> res;
for(int i = 0; i < this->toRun.size(); ++i)
res.push_back(toRun[i].run());
return res;
}
private:
std::vector<AnInterface<R>> toRun;
};
class AnInstanceClass : public AnInterface<int>
{
int run() { return 1+1; }
};
I'd like to be able to do something like this with them:
int main()
{
std::vector<AnInterface<int>> toRun;
toRun.push_back(AnInstanceClass());
toRun.push_back(AnInstanceClass());
Processor<int> p(toRun);
std::vector<int> p.process();
}
Basically, have a class who's job is to take a list of objects, run them, and then return a list of their results, while being agnostic to the types of objects and results (assuming that the objects have a 'run' function). In Java, I accomplished this with generics and interfaces. I tried implementing the above solution in C++, but it doesn't compile and the compiler output is very cryptic, suggesting that I'm screwing up something very fundamental to the language. My C++ is a little rusty, so I'm not exactly sure what that is. How can something like this be implemented in C++?
Edit: Here's the error message when I try to compile the above code:
In file included from /usr/include/c++/4.8/vector:64:0,
from test.cpp:1:
/usr/include/c++/4.8/bits/stl_vector.h: In instantiation of ‘class std::vector<AnInterface<int> >’:
test.cpp:36:36: required from here
/usr/include/c++/4.8/bits/stl_vector.h:704:7: error: cannot allocate an object of abstract type ‘AnInterface<int>’
resize(size_type __new_size, value_type __x = value_type())
^
test.cpp:4:7: note: because the following virtual functions are pure within ‘AnInterface<int>’:
class AnInterface
^
test.cpp:7:19: note: R AnInterface<R>::run() [with R = int]
virtual R run() = 0;
^
test.cpp: In function ‘int main()’:
test.cpp:40:23: error: expected initializer before ‘.’ token
std::vector<int> p.process();
^
test.cpp: In instantiation of ‘Processor<R>::Processor(std::vector<AnInterface<R> >) [with R = int]’:
test.cpp:39:27: required from here
test.cpp:15:68: error: no matching function for call to ‘std::vector<int, std::allocator<int> >::vector(std::vector<AnInterface<int> >&)’
Processor(std::vector<AnInterface<R> > toRun) : toRun(toRun) {}
^
test.cpp:15:68: note: candidates are:
In file included from /usr/include/c++/4.8/vector:64:0,
from test.cpp:1:
/usr/include/c++/4.8/bits/stl_vector.h:398:9: note: template<class _InputIterator> std::vector<_Tp, _Alloc>::vector(_InputIterator, _InputIterator, const allocator_type&)
vector(_InputIterator __first, _InputIterator __last,
^
/usr/include/c++/4.8/bits/stl_vector.h:398:9: note: template argument deduction/substitution failed:
test.cpp:15:68: note: candidate expects 3 arguments, 1 provided
Processor(std::vector<AnInterface<R> > toRun) : toRun(toRun) {}
^
In file included from /usr/include/c++/4.8/vector:64:0,
from test.cpp:1:
/usr/include/c++/4.8/bits/stl_vector.h:310:7: note: std::vector<_Tp, _Alloc>::vector(const std::vector<_Tp, _Alloc>&) [with _Tp = int; _Alloc = std::allocator<int>]
vector(const vector& __x)
^
/usr/include/c++/4.8/bits/stl_vector.h:310:7: note: no known conversion for argument 1 from ‘std::vector<AnInterface<int> >’ to ‘const std::vector<int, std::allocator<int> >&’
/usr/include/c++/4.8/bits/stl_vector.h:295:7: note: std::vector<_Tp, _Alloc>::vector(std::vector<_Tp, _Alloc>::size_type, const value_type&, const allocator_type&) [with _Tp = int; _Alloc = std::allocator<int>; std::vector<_Tp, _Alloc>::size_type = long unsigned int; std::vector<_Tp, _Alloc>::value_type = int; std::vector<_Tp, _Alloc>::allocator_type = std::allocator<int>]
vector(size_type __n, const value_type& __value = value_type(),
^
/usr/include/c++/4.8/bits/stl_vector.h:295:7: note: no known conversion for argument 1 from ‘std::vector<AnInterface<int> >’ to ‘std::vector<int, std::allocator<int> >::size_type {aka long unsigned int}’
/usr/include/c++/4.8/bits/stl_vector.h:256:7: note: std::vector<_Tp, _Alloc>::vector(const allocator_type&) [with _Tp = int; _Alloc = std::allocator<int>; std::vector<_Tp, _Alloc>::allocator_type = std::allocator<int>]
vector(const allocator_type& __a)
^
/usr/include/c++/4.8/bits/stl_vector.h:256:7: note: no known conversion for argument 1 from ‘std::vector<AnInterface<int> >’ to ‘const allocator_type& {aka const std::allocator<int>&}’
/usr/include/c++/4.8/bits/stl_vector.h:248:7: note: std::vector<_Tp, _Alloc>::vector() [with _Tp = int; _Alloc = std::allocator<int>]
vector()
^
/usr/include/c++/4.8/bits/stl_vector.h:248:7: note: candidate expects 0 arguments, 1 provided
In file included from /usr/include/c++/4.8/vector:69:0,
from test.cpp:1:
/usr/include/c++/4.8/bits/vector.tcc: In instantiation of ‘void std::vector<_Tp, _Alloc>::_M_insert_aux(std::vector<_Tp, _Alloc>::iterator, const _Tp&) [with _Tp = AnInterface<int>; _Alloc = std::allocator<AnInterface<int> >; std::vector<_Tp, _Alloc>::iterator = __gnu_cxx::__normal_iterator<AnInterface<int>*, std::vector<AnInterface<int> > >; typename std::_Vector_base<_Tp, _Alloc>::pointer = AnInterface<int>*]’:
/usr/include/c++/4.8/bits/stl_vector.h:913:28: required from ‘void std::vector<_Tp, _Alloc>::push_back(const value_type&) [with _Tp = AnInterface<int>; _Alloc = std::allocator<AnInterface<int> >; std::vector<_Tp, _Alloc>::value_type = AnInterface<int>]’
test.cpp:37:38: required from here
/usr/include/c++/4.8/bits/vector.tcc:329:19: error: cannot allocate an object of abstract type ‘AnInterface<int>’
_Tp __x_copy = __x;
^
test.cpp:4:7: note: since type ‘AnInterface<int>’ has pure virtual functions
class AnInterface
^
In file included from /usr/include/c++/4.8/vector:69:0,
from test.cpp:1:
/usr/include/c++/4.8/bits/vector.tcc:329:8: error: cannot declare variable ‘__x_copy’ to be of abstract type ‘AnInterface<int>’
_Tp __x_copy = __x;
^
test.cpp:4:7: note: since type ‘AnInterface<int>’ has pure virtual functions
class AnInterface
^
In file included from /usr/include/x86_64-linux-gnu/c++/4.8/bits/c++allocator.h:33:0,
from /usr/include/c++/4.8/bits/allocator.h:46,
from /usr/include/c++/4.8/vector:61,
from test.cpp:1:
/usr/include/c++/4.8/ext/new_allocator.h: In instantiation of ‘void __gnu_cxx::new_allocator<_Tp>::construct(__gnu_cxx::new_allocator<_Tp>::pointer, const _Tp&) [with _Tp = AnInterface<int>; __gnu_cxx::new_allocator<_Tp>::pointer = AnInterface<int>*]’:
/usr/include/c++/4.8/ext/alloc_traits.h:216:9: required from ‘static void __gnu_cxx::__alloc_traits<_Alloc>::construct(_Alloc&, __gnu_cxx::__alloc_traits<_Alloc>::pointer, const _Tp&) [with _Tp = AnInterface<int>; _Alloc = std::allocator<AnInterface<int> >; __gnu_cxx::__alloc_traits<_Alloc>::pointer = AnInterface<int>*]’
/usr/include/c++/4.8/bits/stl_vector.h:906:34: required from ‘void std::vector<_Tp, _Alloc>::push_back(const value_type&) [with _Tp = AnInterface<int>; _Alloc = std::allocator<AnInterface<int> >; std::vector<_Tp, _Alloc>::value_type = AnInterface<int>]’
test.cpp:37:38: required from here
/usr/include/c++/4.8/ext/new_allocator.h:130:9: error: cannot allocate an object of abstract type ‘AnInterface<int>’
{ ::new((void *)__p) _Tp(__val); }
^
test.cpp:4:7: note: since type ‘AnInterface<int>’ has pure virtual functions
class AnInterface
^
In file included from /usr/include/c++/4.8/vector:62:0,
from test.cpp:1:
/usr/include/c++/4.8/bits/stl_construct.h: In instantiation of ‘void std::_Construct(_T1*, const _T2&) [with _T1 = AnInterface<int>; _T2 = AnInterface<int>]’:
/usr/include/c++/4.8/bits/stl_uninitialized.h:75:53: required from ‘static _ForwardIterator std::__uninitialized_copy<_TrivialValueTypes>::__uninit_copy(_InputIterator, _InputIterator, _ForwardIterator) [with _InputIterator = __gnu_cxx::__normal_iterator<const AnInterface<int>*, std::vector<AnInterface<int> > >; _ForwardIterator = AnInterface<int>*; bool _TrivialValueTypes = false]’
/usr/include/c++/4.8/bits/stl_uninitialized.h:117:41: required from ‘_ForwardIterator std::uninitialized_copy(_InputIterator, _InputIterator, _ForwardIterator) [with _InputIterator = __gnu_cxx::__normal_iterator<const AnInterface<int>*, std::vector<AnInterface<int> > >; _ForwardIterator = AnInterface<int>*]’
/usr/include/c++/4.8/bits/stl_uninitialized.h:258:63: required from ‘_ForwardIterator std::__uninitialized_copy_a(_InputIterator, _InputIterator, _ForwardIterator, std::allocator<_Tp>&) [with _InputIterator = __gnu_cxx::__normal_iterator<const AnInterface<int>*, std::vector<AnInterface<int> > >; _ForwardIterator = AnInterface<int>*; _Tp = AnInterface<int>]’
/usr/include/c++/4.8/bits/stl_vector.h:316:32: required from ‘std::vector<_Tp, _Alloc>::vector(const std::vector<_Tp, _Alloc>&) [with _Tp = AnInterface<int>; _Alloc = std::allocator<AnInterface<int> >]’
test.cpp:39:27: required from here
/usr/include/c++/4.8/bits/stl_construct.h:83:7: error: cannot allocate an object of abstract type ‘AnInterface<int>’
::new(static_cast<void*>(__p)) _T1(__value);
^
test.cpp:4:7: note: since type ‘AnInterface<int>’ has pure virtual functions
class AnInterface
You're basically (attempting to) re-create the functionality of std::generate. The difference is that generate doesn't rely on the somewhat clunky convention of a member function named run. Rather, it invokes something like a function (though it may, and often will, be an overloaded operator()).
We can also (frequently) avoid the separate definition of what you've named AnInstanceClass by defining the class in a lambda expression.
So, in this case, we'd be looking at something like:
std::vector<int> p;
std::generate_n(std::back_inserter(p), 2, [] { return 1 + 1; });
This is basically threading-agnostic, so if you want to run the individual tasks in separate threads, you can do that pretty easily as well. There are some caveats with std::async, but they're pretty much the same regardless of whether you involve std::generate.
Note that this is slightly different from #Severin's answer--he's mentioning std::transform instead of std::generate. The basic difference between the two is that transform takes a set of inputs, transforms them, and produces a set of those outputs. Your AnInstance::run just produces outputs (without taking any inputs) so at least to me it seems like std::generate is a better fit.
std::transform would be more useful if you had something like this:
std::vector<int> inputs { 1, 2, 3, 4, 5};
std::vector<int> results;
std::transform(inputs.begin(), inputs.end(), [](int in) { return in * 2; });
This should produce results of 2, 4, 6, 8, 10.
The only conceptual error you have is trying to get polymorphic behaviour when invoking virtual functions through objects, as opposed to pointers or references to said objects. In C++, to get run-time polymorphism, you need to work with pointers or references. Thus, Processor should work with a std::vector<AnInterface<R>*> like this:
template<typename R>
class Processor
{
public:
Processor(std::vector<AnInterface<R>*> toRun) : toRun(toRun) {}
std::vector<R> process() {
std::vector<R> res;
for(int i = 0; i < this->toRun.size(); ++i)
res.push_back(toRun[i]->run());
return res;
}
private:
std::vector<AnInterface<R>*> toRun;
};
Here's a fixed version of your code.
Another thing to note : when using overriding a virtual function in a derived class, mark the override with the eponymous keyword. This helps the compiler help you.
Do you really need Processor class? What I would propose to use std::transform
std::transform applies the given function to a range and stores the result in another range
vector<AnInterface<R>> does not work because it causes slicing. This is also the cause of your error messages, because some vector operations require to default-construct or copy-construct objects and that is not possible with an abstract class.
Probably vector<shared_ptr<AnInterface<R>>> best matches your intent. shared_ptr is the closest thing C++ has to a Java object reference.
Here is working code in C++11 based on your sample code. One point I would have is that Processor currently takes its vector by value. It could take this by reference, or even by moving, if that better matched your design.
#include <iostream>
#include <memory>
#include <vector>
template<typename R>
struct AnInterface
{
virtual R run() = 0;
virtual ~AnInterface() {}
};
template<typename R>
using AnInterfaceVector = std::vector< std::shared_ptr<AnInterface<R>> >;
template<typename R>
class Processor
{
public:
Processor(AnInterfaceVector<R> toRun) : toRun(toRun) {}
std::vector<R> process()
{
std::vector<R> res;
for (auto && r : toRun)
res.push_back( r->run() );
return res;
}
private:
AnInterfaceVector<R> toRun;
};
struct AnInstanceClass : AnInterface<int>
{
int run() override { return temp; }
AnInstanceClass(int n): temp(n) {}
int temp;
};
int main()
{
AnInterfaceVector<int> toRun;
toRun.emplace_back( std::make_shared<AnInstanceClass>(4) );
toRun.emplace_back( std::make_shared<AnInstanceClass>(7) );
Processor<int> p{toRun};
auto results = p.process();
for (auto && i : results)
std::cout << i << " ";
std::cout << std::endl;
}
NB. I don't offer any claim whether this is better or worse than using a different pattern as other answers have suggested; this is just a working version of the code you were trying to write.
As was already mentioned in the other answers, your error was trying to use a vector of interfaces (std::vector<AnInterface<int>>) instead of a vector of pointers to interfaces like std::vector<AnInterface<int>*> - with only the latter allowing polymorphism, whereas your version would try to store actual Interface objects (which is of course not posssible as they are abstract classes).
I wanted to mention in addition, that there is a nice pattern by Sean Parent that makes it unnecessary for your AnInstanceClass to inhereit from anything, as long as it implements a member function with the correct name and signature. This is quite handy, because you can e.g. even use lambdas or plain functions (after wrapping them in a std::function) which cannot inherit from anything:
#include <vector>
#include <memory>
#include <iostream>
#include <algorithm>
#include <functional>
//R is the return type
template<class R>
class Processor {
public:
//T can be anything, that has an ()-operator
template<class T>
void push_back(const T& arg) {
todo.emplace_back(std::make_unique<runnable_imp<T>>(arg));
}
std::vector<R> process() {
std::vector<R> ret;
for (auto& e : todo) {
ret.push_back(e->run());
}
return ret;
}
private:
struct runnable_concept {
virtual R run()=0;
virtual ~runnable_concept(){};
};
template<class T>
struct runnable_imp :public runnable_concept {
runnable_imp(T data) :data(data){};
virtual R run() override { return data(); }
T data;
};
std::vector<std::unique_ptr<runnable_concept>> todo;
};
struct SomeClass {
SomeClass(int arg) :arg(arg){};
int operator()(){ return arg; }
int arg;
};
int SomeFunction(){ return 30; }
int main()
{
Processor<int> pr;
pr.push_back([]{return 10; });
pr.push_back(SomeClass(20));
pr.push_back(std::function<int()>(SomeFunction));
std::vector<int> res= pr.process();
for (auto e : res) {
std::cout << e << std::endl;
}
}
Here is a very straightforward piece of code:
#include <vector>
int main() {
std::vector<int> myVec(5);
std::vector<int>::const_iterator first = myVec.begin();
std::vector<int>::const_iterator last = myVec.begin() + 3;
std::vector<int> newVec1(first, last);
std::vector<int> newVec2(myVec.begin(), last);
return 0;
}
Line declaring newVec1 compiles.
Line declaring newVec2 fails with following error:
prog.cpp: In function 'int main()':
prog.cpp:11:49: error: no matching function for call to 'std::vector<int>::vector(std::vector<int>::iterator, std::vector<int>::const_iterator&)'
std::vector<int> newVec2(myVec.begin(), last);
^
prog.cpp:11:49: note: candidates are:
In file included from /usr/include/c++/4.9/vector:64:0,
from prog.cpp:3:
/usr/include/c++/4.9/bits/stl_vector.h:401:9: note: template<class _InputIterator, class> std::vector<_Tp, _Alloc>::vector(_InputIterator, _InputIterator, const allocator_type&)
vector(_InputIterator __first, _InputIterator __last,
^
/usr/include/c++/4.9/bits/stl_vector.h:401:9: note: template argument deduction/substitution failed:
prog.cpp:11:49: note: deduced conflicting types for parameter '_InputIterator' ('__gnu_cxx::__normal_iterator<int*, std::vector<int> >' and '__gnu_cxx::__normal_iterator<const int*, std::vector<int> >')
std::vector<int> newVec2(myVec.begin(), last);
^
In file included from /usr/include/c++/4.9/vector:64:0,
from prog.cpp:3:
/usr/include/c++/4.9/bits/stl_vector.h:373:7: note: std::vector<_Tp, _Alloc>::vector(std::initializer_list<_Tp>, const allocator_type&) [with _Tp = int; _Alloc = std::allocator<int>; std::vector<_Tp, _Alloc>::allocator_type = std::allocator<int>]
vector(initializer_list<value_type> __l,
^
/usr/include/c++/4.9/bits/stl_vector.h:373:7: note: no known conversion for argument 1 from 'std::vector<int>::iterator {aka __gnu_cxx::__normal_iterator<int*, std::vector<int> >}' to 'std::initializer_list<int>'
/usr/include/c++/4.9/bits/stl_vector.h:348:7: note: std::vector<_Tp, _Alloc>::vector(std::vector<_Tp, _Alloc>&&, const allocator_type&) [with _Tp = int; _Alloc = std::allocator<int>; std::vector<_Tp, _Alloc>::allocator_type = std::allocator<int>]
vector(vector&& __rv, const allocator_type& __m)
^
/usr/include/c++/4.9/bits/stl_vector.h:348:7: note: no known conversion for argument 1 from 'std::vector<int>::iterator {aka __gnu_cxx::__normal_iterator<int*, std::vector<int> >}' to 'std::vector<int>&&'
/usr/include/c++/4.9/bits/stl_vector.h:339:7: note: std::vector<_Tp, _Alloc>::vector(const std::vector<_Tp, _Alloc>&, const allocator_type&) [with _Tp = int; _Alloc = std::allocator<int>; std::vector<_Tp, _Alloc>::allocator_type = std::allocator<int>]
vector(const vector& __x, const allocator_type& __a)
^
/usr/include/c++/4.9/bits/stl_vector.h:339:7: note: no known conversion for argument 1 from 'std::vector<int>::iterator {aka __gnu_cxx::__normal_iterator<int*, std::vector<int> >}' to 'const std::vector<int>&'
/usr/include/c++/4.9/bits/stl_vector.h:335:7: note: std::vector<_Tp, _Alloc>::vector(std::vector<_Tp, _Alloc>&&) [with _Tp = int; _Alloc = std::allocator<int>]
vector(vector&& __x) noexcept
^
/usr/include/c++/4.9/bits/stl_vector.h:335:7: note: candidate expects 1 argument, 2 provided
/usr/include/c++/4.9/bits/stl_vector.h:318:7: note: std::vector<_Tp, _Alloc>::vector(const std::vector<_Tp, _Alloc>&) [with _Tp = int; _Alloc = std::allocator<int>]
vector(const vector& __x)
^
/usr/include/c++/4.9/bits/stl_vector.h:318:7: note: candidate expects 1 argument, 2 provided
/usr/include/c++/4.9/bits/stl_vector.h:289:7: note: std::vector<_Tp, _Alloc>::vector(std::vector<_Tp, _Alloc>::size_type, const value_type&, const allocator_type&) [with _Tp = int; _Alloc = std::allocator<int>; std::vector<_Tp, _Alloc>::size_type = unsigned int; std::vector<_Tp, _Alloc>::value_type = int; std::vector<_Tp, _Alloc>::allocator_type = std::allocator<int>]
vector(size_type __n, const value_type& __value,
^
/usr/include/c++/4.9/bits/stl_vector.h:289:7: note: no known conversion for argument 1 from 'std::vector<int>::iterator {aka __gnu_cxx::__normal_iterator<int*, std::vector<int> >}' to 'std::vector<int>::size_type {aka unsigned int}'
/usr/include/c++/4.9/bits/stl_vector.h:277:7: note: std::vector<_Tp, _Alloc>::vector(std::vector<_Tp, _Alloc>::size_type, const allocator_type&) [with _Tp = int; _Alloc = std::allocator<int>; std::vector<_Tp, _Alloc>::size_type = unsigned int; std::vector<_Tp, _Alloc>::allocator_type = std::allocator<int>]
vector(size_type __n, const allocator_type& __a = allocator_type())
^
/usr/include/c++/4.9/bits/stl_vector.h:277:7: note: no known conversion for argument 1 from 'std::vector<int>::iterator {aka __gnu_cxx::__normal_iterator<int*, std::vector<int> >}' to 'std::vector<int>::size_type {aka unsigned int}'
/usr/include/c++/4.9/bits/stl_vector.h:264:7: note: std::vector<_Tp, _Alloc>::vector(const allocator_type&) [with _Tp = int; _Alloc = std::allocator<int>; std::vector<_Tp, _Alloc>::allocator_type = std::allocator<int>]
vector(const allocator_type& __a) _GLIBCXX_NOEXCEPT
^
/usr/include/c++/4.9/bits/stl_vector.h:264:7: note: candidate expects 1 argument, 2 provided
/usr/include/c++/4.9/bits/stl_vector.h:253:7: note: std::vector<_Tp, _Alloc>::vector() [with _Tp = int; _Alloc = std::allocator<int>]
vector()
^
/usr/include/c++/4.9/bits/stl_vector.h:253:7: note: candidate expects 0 arguments, 2 provided
Both g++ and Visual Studio fail to compile this, any idea why?
myVec.begin() is the same as first...
myVec.begin() is not the same as first. first is of type std::vector<int>::const_iterator, whereas myVec.begin() is of type std::vector<int>::iterator.
If you want a const iterator, use cbegin:
std::vector<int> newVec2(myVec.cbegin(), last);
vec.begin() is not the same as first because it will return you an iterator, not a const_iterator.
This happens because having a const or non-const iterator depends on the type of the access you have to the container, not on the use you wish to do with the iterator and it's also the reason for which for example creating proxies is the only way to separate read from write operations in array-like objects ::operator[] instead of just rely on const or non-const version.
It's just of the many cases in which the const-correctness concept shows its limits.