Extremely new to c++ however have a question regarding templates
Suppose I have a simple template class as defined below:
template<typename Collection>
class MySack {
private:
Collection c;
public:
typedef typename Collection::value_type value_type;
void add(const value_type& value) {
c.push_back(value);
}
};
The aim of the class being to accept any type of collection, and allow a user to insert the correct type of value for the specified typename Collection
.
The obvious problem is that this is only going to work for types which have a push_back
method defined, which means it would work with list
however not with set
.
I started reading about template specialization to see if that'd be any help, however I don't think this would provide a solution as the type contained within the set would have to be known.
How would this problem be approached in c++?
The wrapper is C++ but exposes itself as C. Meaning the exposed C functions are C functions. However, this needs to be compiled as C++ in order to use new and delete for proper C++ class initialization. The wrapper creates a struct and puts the CPPMather object into it as as void pointer. This struct allows us to use it in a type safe manner in C.
Wrapping C++ objects in C 1 Introduction. Using C functions from C++ is very easy but going the other way isn’t. ... 2 The C++ Object We Want to Wrap 3 The Wrapper. The wrapper is C++ but exposes itself as C. ... 4 Example application. The C file (main.c) will use the C functions exposed by the wrapper’s header file. 5 Build and Running. ...
Our wrapper class has to: The first thing that our wrapper needs to provide are uniform interfaces, in our case, it is int (*behavior) (). Our wrapper class has to manage the lifetime of the objects to avoid using dangling references or pointers.
In C++ you can achieve this with Templates. Using templates we can ask the compiler to generate functions/classes for us based on the types used in our code. This helps us to not write similar blocks of code multiple times to keep our code neat. Here’s a simple example:
You can use std::experimental::is_detected
and if constexpr
to make it work:
template<class C, class V>
using has_push_back_impl = decltype(std::declval<C>().push_back(std::declval<V>()));
template<class C, class V>
constexpr bool has_push_back = std::experimental::is_detected_v<has_push_back_impl, C, V>;
template<typename Collection>
class MySack {
private:
Collection c;
public:
typedef typename Collection::value_type value_type;
void add(const value_type& value) {
if constexpr (has_push_back<Collection, value_type>) {
std::cout << "push_back.\n";
c.push_back(value);
} else {
std::cout << "insert.\n";
c.insert(value);
}
}
};
int main() {
MySack<std::set<int>> f;
f.add(23);
MySack<std::vector<int>> g;
g.add(23);
}
You can switch to insert
member function, which has the same syntax for std::vector
, std::set
, std::list
, and other containers:
void add(const value_type& value) {
c.insert(c.end(), value);
}
In C++11, you might also want to create a version for rvalue arguments:
void add(value_type&& value) {
c.insert(c.end(), std::move(value));
}
And, kind-of simulate emplace semantics (not truly in fact):
template <typename... Ts>
void emplace(Ts&&... vs) {
c.insert(c.end(), value_type(std::forward<Ts>(vs)...));
}
...
int main() {
using value_type = std::pair<int, std::string>;
MySack<std::vector<value_type>> v;
v.emplace(1, "first");
MySack<std::set<value_type>> s;
s.emplace(2, "second");
MySack<std::list<value_type>> l;
l.emplace(3, "third");
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With