I would like to make a class that inherits of a combination of two templated types like this for example:
#include <vector>
#include <set>
template<typename T, typename Base=std::vector<T>>
class A : public Base
{
};
int main()
{
A<int> a;
A<int, std::set<int>> b;
return 0;
}
But now, I would like to be able to write
A<int, std::set> b;
instead of
A<int, std::set<int>> b;
How can I do this please?
Thanks! :)
SOLUTION from #Piotr S.
Here I repost the complete solution given by Piotr S. so this very nice answer is easier to find for others (don't forget to vote for his answer :-P):
#include <vector>
#include <set>
#include <utility>
#include <iostream>
template <template <typename...> class> struct tag {};
template <typename T, template <typename...> class Base = std::vector>
class A : public Base<T>
{
public:
void add(const T& t)
{
return _add(tag<Base>{}, t);
}
private:
void _add(tag<std::set>, const T& t)
{
std::cout << "set\n";
Base<T>::insert(t);
}
void _add(tag<std::vector>, const T& t)
{
std::cout << "vector\n";
Base<T>::push_back(t);
}
};
int main()
{
A<int> a;
A<int, std::set> b;
a.add(1);
b.add(1);
}
#include <vector>
#include <set>
template <typename T, template <typename...> class Base = std::vector>
class A : public Base<T>
{
};
int main()
{
A<int> a;
A<int, std::set> b;
}
DEMO
Related
I want to have a template function which accepts unary member-function pointers of an instance of some generic type.
My problem is that I must support both void(T val) and void(const T& val) member functions.
I have written one template function for each case and it works fine, but this leads to code duplication since the function logic is completely the same. (I found something completely similar here: Function taking both pointer to member-function and pointer to const member-function but I fail to see a definitive solution).
An example of the generic type mentioned above:
using UserAddress = std::string;
class User
{
private:
int mHeight;
UserAddress mAddress;
public:
void SetHeight(int height){mHeight = height;}
void SetAddress(const UserAddress& address){mAddress = address;}
};
Where UserAddress is some heavy type I want to pass by reference.
My templated function:
template <typename TPersistentObject>
class Persistence
{
private:
std::map<std::string, std::function<void(User*)>> mSetterOfProperty;
template <typename TPersistentObject, typename TPropertyValue>
void DefinePropertySettingMethod(const std::string& propertyName,
void (TPersistentObject::*propertySetter)(TPropertyValue), std::function<TPropertyValue(void)> dataReader)
{
mSetterOfProperty[propertyName] =
[propertySetter, columnDataReader](TPersistentObject* persistentObject)
{
(persistentObject->*propertySetter)(dataReader());
};
}
};
/// Const& implementation leading to code duplication
template <typename TPersistentObject, typename TPropertyValue>
void DefinePropertySettingMethod(const std::string& propertyName,
void (TPersistentObject::*propertySetter)(const TPropertyValue&), std::function<TPropertyValue(void)> dataReader)
{
...
}
};
Is there some way to define this function to support the following:
int main()
{
auto intDataReader = []() {
return 1;
};
auto stringDataReader = []() {
return UserAddress("Next Door");
};
Persistence p;
p.DefinePropertySettingMethod<User,int>("Height", &User::SetHeight, intDataReader);
p.DefinePropertySettingMethod<User,UserAddress>("Address", &User::SetAddress, stringDataReader);
}
Thanks to Igor Tandetnik 's tip I managed to compile a solution. std::enable_if is not what I needed though since I did not need to deactivate an overload (or at least I couldn't come to a solution using it).
std::conditional did the trick.
Here is the code:
#include <string>
#include <functional>
#include <map>
#include <string>
#include <type_traits>
using UserAddress = std::string;
class User
{
private:
int mHeight;
UserAddress mAddress;
public:
void SetHeight(int height){mHeight = height;}
void SetAddress(const UserAddress& address){mAddress = address;}
};
template <typename TPersistentObject>
class Persistence
{
public:
std::map<std::string, std::function<void(TPersistentObject*)>> mSetterOfProperty;
template <typename TPropertyValue>
void DefinePropertySettingMethod(const std::string& propertyName,
void (TPersistentObject::*propertySetter)(TPropertyValue),
std::function<
typename std::conditional<!std::is_same<TPropertyValue, typename std::decay<TPropertyValue>::type>::value,
typename std::decay<TPropertyValue>::type, TPropertyValue>::type
(void)> dataReader)
{
mSetterOfProperty[propertyName] =
[propertySetter, dataReader](TPersistentObject* persistentObject)
{
(persistentObject->*propertySetter)(dataReader());
};
}
};
int main()
{
std::function<int()> intDataReader = []() {
return 1;
};
std::function<std::string()> stringDataReader = []() {
return UserAddress("Next Door");
};
Persistence<User> p;
p.DefinePropertySettingMethod("Height", &User::SetHeight, intDataReader);
p.DefinePropertySettingMethod("Address", &User::SetAddress, stringDataReader);
}
How can I have this effect without the arbitrary typedefs?
#include <type_traits>
#include <iostream>
typedef int Primary;
typedef float Secondary;
template<Class C, std::enable_if<std::is_same<Class, Primary>::value || std::is_same<Class, Secondary>::value> = 0>
class Entity {
public:
template<std::enable_if<std::is_same<Class, Secondary>::value>::type = 0>
void onlyLegalForSecondaryEntities() {
std::cout << "Works" << std::endl;
}
};
int main() {
Entity<Secondary> e;
e.onlyLegalForSecondaryEntities();
return 0;
}
Is there a more elegant way to produce this so that Entity can only be instantiated with Primary or Secondary as template arguments?
After fixing the errors in your code:
In C++1z you can easily roll a trait is_any with std::disjunction:
template<typename T, typename... Others>
struct is_any : std::disjunction<std::is_same<T, Others>...>
{
};
In C++11, you can implement disjuncation as
template<class...> struct disjunction : std::false_type { };
template<class B1> struct disjunction<B1> : B1 { };
template<class B1, class... Bn>
struct disjunction<B1, Bn...>
: std::conditional<B1::value != false, B1, disjunction<Bn...>>::type { };
Then define your class template as
template<class C, typename std::enable_if<is_any<C, Primary, Secondary>::value>::type* = nullptr>
class Entity {
public:
template<typename std::enable_if<std::is_same<C, Secondary>::value>::type* = nullptr>
void onlyLegalForSecondaryEntities() {
std::cout << "Works" << std::endl;
}
};
demo
You can take this further and make enable_if_any alias that would resolve to void if possible:
template<typename This, typename... Elems>
using enable_if_is_any = typename std::enable_if<is_any<This, Elems...>::value>::type;
template<class C, enable_if_is_any<C, Primary, Secondary>* = nullptr>
class Entity {
public:
template<typename std::enable_if<std::is_same<C, Secondary>::value>::type* = nullptr>
void onlyLegalForSecondaryEntities() {
std::cout << "Works" << std::endl;
}
};
demo
why this code compiled in c++11 the it2 and it4 is correct.
but it1 and it3 is compiled error with non-static function?
is it bug with std compiler? or i understand the wrong about these?
#include <iostream>
#include <functional>
using namespace std;
namespace test {
class a {
public:
void funa() {}
};
class b : public a {
public:
void funb() { }
};
struct c {
void func() {}
};
}
int main()
{
test::b b1;
test::c c1;
auto it1 = std::bind(&(test::c::func),&c1);
auto it2 = std::bind(&test::c::func,&c1);
auto it3 = std::bind(&test::a::funa, &b1);
auto it4 = std::bind(&(test::a::funa),&b1);
}
I would try to make my point clear with an example:
We have
template <class RandomAccessIterator>
void sort (RandomAccessIterator first, RandomAccessIterator last);
But I'm thinking if it is ok to make it more convenient:
template <typename T> void sort(std::vector<T>& container) {
std::sort( container.begin(), container.end() );
}
template <typename T> void sort(std::list<T>& container);
template <typename T> void sort(std::array<T>& container);
//e.t.c
You know there are many container types, it is possible to code once for all the container types?
void sort(ContainerType<ElementType> &container);
//and container should have begin() and end() methods,
//otherwise the compiler would warn me.
You are talking about concepts in C++. The idea is discussed for a long time for now, but they are still not in the standard. See here:
template<Sortable Cont>
void sort(Cont& container);
The work is close to the end for now, several experimental implementations are already available, and we expect them to hit C++17, hopefully. The nicest thing about concepts is their straightforward error messages:
list<int> lst = ...; // oops, bidirectional iterators
sort(lst); // error: 'T' is not a/an 'Sortable' type
In modern compilers, errors related to templatized code are very confusing. Compare with this example, compiled with Visual Studio 2013:
std::list<int> l;
std::sort(l.begin(), l.end());
// error C2784: 'unknown-type std::operator -(std::move_iterator<_RanIt> &,const std::move_iterator<_RanIt2> &)' : could not deduce template argument for 'std::move_iterator<_RanIt> &' from 'std::_List_iterator<std::_List_val<std::_List_simple_types<int>>>'
// error C2784: 'unknown-type std::operator -(const std::reverse_iterator<_RanIt> &,const std::reverse_iterator<_RanIt2> &)' : could not deduce template argument for 'const std::reverse_iterator<_RanIt> &' from 'std::_List_iterator<std::_List_val<std::_List_simple_types<int>>>'
// error C2784: 'unknown-type std::operator -(const std::_Revranit<_RanIt,_Base> &,const std::_Revranit<_RanIt2,_Base2> &)' : could not deduce template argument for 'const std::_Revranit<_RanIt,_Base> &' from 'std::_List_iterator<std::_List_val<std::_List_simple_types<int>>>'
There is even a tag on SO: c++-concepts.
Its easy to write a sort function that works for any container. Just write:
template<class C>
void sort(C& container) {
std::sort( container.begin(), container.end() );
}
However, if you want your sort function to be picked ONLY for containers, it becomes a little bit more difficult: As concepts are not yet available, you have to write your own type trait for all containers and use SFINAE.
Edit: Come to think of it, as any class with a random access iterator is probably a container anyway, it should be enough to write the following (without the need for a container trait):
#include <type_traits>
template<class C>
typename std::enable_if<std::is_same<typename std::iterator_traits<typename C::iterator>::iterator_category, std::random_access_iterator_tag>{}>::type
sort(C& container) {
std::sort( container.begin(), container.end() );
}
#include <iterator>
#include <utility>
#include <type_traits>
namespace detail
{
using std::begin;
template <typename T, typename = void>
struct has_begin : std::false_type {};
template <typename T>
struct has_begin<T, decltype(void(begin(std::declval<T&>())))> : std::true_type {};
using std::end;
template <typename T, typename = void>
struct has_end : std::false_type {};
template <typename T>
struct has_end<T, decltype(void(end(std::declval<T&>())))> : std::true_type {};
}
template <typename T> using has_begin = detail::has_begin<T>;
template <typename T> using has_end = detail::has_end<T>;
Usage:
template <typename ContainerType>
void sort(ContainerType& container)
{
static_assert(has_begin<ContainerType>{} && has_end<ContainerType>{},
"Invalid container type");
}
Tests:
#include <vector>
#include <list>
namespace X
{
struct A {};
A* begin(A&) { return {}; }
A* end(A&) { return {}; }
}
struct B {};
int main()
{
std::vector<int> v; sort(v); // OK
std::list<int> l; sort(l); // OK
X::A a; sort(a); // OK
int arr[3]{}; sort(arr); // OK
B b; sort(b); // error: Invalid container type
}
DEMO
At the time of answering this question, MikeMB's answer gives the approach but doesn't compile. But here is my attempt. A much lesser complicated approach. You will have to overload SortHelper to accept comparator as well.
#include <iostream>
#include <vector>
#include <list>
#include <iterator>
#include <algorithm>
template<typename C>
void SortHelper(C& container, std::random_access_iterator_tag)
{
std::sort(std::begin(container), std::end(container));
}
template<typename C>
void SortHelper(C& container, std::bidirectional_iterator_tag)
{
container.sort();
}
template<class C>
void sort(C& container)
{
SortHelper(container, typename std::iterator_traits<typename C::iterator>::iterator_category());
}
int main()
{
std::vector<int> ints1 { 3, 2, 1 };
std::list<int> ints2 { 3, 2, 1 };
sort(ints1);
sort(ints2);
std::cout << "printing ints1\n";
for (auto e : ints1 ) { std::cout << e << "\n" ; }
std::cout << "printing ints2\n";
for (auto e : ints2 ) { std::cout << e << "\n" ; }
}
Output
printing ints1
1
2
3
printing ints2
1
2
3
I have the following code:
#include <iostream>
#include <functional>
class test
{
public:
typedef std::function<bool(int)> Handler;
void handler(Handler h){h(5);}
};
class test2
{
public:
template< typename Ret2, typename Ret, typename Class, typename Param>
inline Ret2 MemFn(Ret (Class::*f)(Param), int arg_num)
{
if (arg_num == 1)
return std::bind(f, this, std::placeholders::_1);
}
bool f(int x){ std::cout << x << std::endl; return true;}
};
int main()
{
test t;
test2 t2;
t.handler(t2.MemFn<test::Handler>(&test2::f, 1));
return 0;
}
It works as expected.
I would like to be able to call this:
t.handler(t2.MemFn<test::Handler>(&test2::f));
instead of
t.handler(t2.MemFn<test::Handler>(&test2::f, 1));
Basically I need MemFn to determine in runtime what Handler expects as the number of arguments.
Is that even possible?
You may create some type_traits to have your info, something like:
template <typename T> struct function_trait;
template <typename Ret, typename ... Args>
struct function_trait<std::function<Ret(Args...)>>
{
static constexpr std::size_t args_count = sizeof...(Args);
};
And so your method may look like:
template<typename Ret2, typename Ret, typename Class, typename Param>
inline Ret2 MemFn(Ret (Class::*f)(Param))
{
if (function_trait<Ret2>::args_count == 1)
return std::bind(f, this, std::placeholders::_1);
throw std::runtime_error("invalid number of arguments");
}