This is a bit long for a comment, so I'll post this as an answer:
class EG
{
template<class T>
std::function<void(T)> f; // a data member template?
template<class T>
void func(std::function<void(T)>&& arg)
{
f = nullptr;
f.swap(arg);
// could as well just
// f = std::move(arg);
}
};
If your use case is:
EG obj;
obj.func( [](int i) { std::cout << i; } );
obj.f(42);
obj.func( [](string s) { std::cout << s; } );
obj.f("hello world");
Than this is highly error-prone, as the compiler cannot check the types of the argument of the function call obj.f
as the argument types are supposed to be variable: If you assign a void(int)
via obj.func
, than it shall accept an int; if you assign a void(string)
than it shall accept a string.
There are no data member templates in C++ (IMO doesn't make sense to have them - when should they be instantiated?), but it is possible to get the behaviour of the use case above.
A simpler, less error-prone solution:
#include <functional>
#include <string>
#include <iostream>
#include <memory>
using std::string;
struct any_function
{
virtual ~any_function()
{}
};
template < typename T >
struct concrete_function
: public any_function
{
T m;
concrete_function(T&& p)
: m( std::move(p) )
{}
virtual ~concrete_function() override
{}
};
struct EG
{
private:
std::shared_ptr<any_function> fs;
public:
EG& operator=(std::function<void(int)>&& p) // do NOT templatize this!!!!
{
using stor_type = concrete_function< std::function<void(int)> >;
fs = std::make_shared<stor_type>( std::move(p) );
return *this;
}
EG& operator=(std::function<void(string)>&& p) // do NOT templatize this!!!!
{
using stor_type = concrete_function< std::function<void(string)> >;
fs = std::make_shared<stor_type>( std::move(p) );
return *this;
}
void operator()(int p) // do NOT templatize this!!!!
{
using stor_type = concrete_function< std::function<void(int)> >;
auto const& ptr = std::static_pointer_cast< stor_type >(fs);
if(ptr) { ptr->m(p); }
else { /* throw something */ }
}
void operator()(string p) // do NOT templatize this!!!!
{
using stor_type = concrete_function< std::function<void(string)> >;
auto const& ptr = std::static_pointer_cast< stor_type >(fs);
if(ptr) { ptr->m(p); }
else { /* throw something */ }
}
};
int main()
{
EG obj;
obj = [](int i) { std::cout << i; };
obj(42); // using overload mechanism
}
You shouldn't templatize the marked functions as the type used in the creation (make_shared
) has to match exactly the type used in the call (static_pointer_cast
).
Or, using enums:
#include <functional>
#include <string>
#include <iostream>
using std::string;
struct EG
{
enum stored_function_type
{
INT_FUNC, STRING_FUNC
};
union function_storage
{
std::function<void(int)> f_int;
std::function<void(string)> f_string;
function_storage()
: f_int()
{}
~function_storage()
{}
};
stored_function_type ft;
function_storage fs;
template < typename T > void destroy(T& p) { p.~T(); }
void destroy()
{
switch(ft)
{
case INT_FUNC:
destroy(fs.f_int);
break;
case STRING_FUNC:
destroy(fs.f_string);
break;
}
}
EG()
: ft(INT_FUNC)
{}
~EG()
{
destroy();
}
EG& operator=(std::function<void(int)>&& p)
{
destroy();
new(&fs.f_int) std::function<void(int)>( std::move(p) );
ft = INT_FUNC;
return *this;
}
EG& operator=(std::function<void(string)>&& p)
{
destroy();
new(&fs.f_string) std::function<void(string)>( std::move(p) );
ft = STRING_FUNC;
return *this;
}
void operator()(int p)
{
// better check ft == INT_FUNC
return fs.f_int(p);
}
void operator()(string p)
{
// better check ft == STRING_FUNC
return fs.f_string(p);
}
};
int main()
{
EG obj;
obj = [](int i) { std::cout << i; };
obj(42); // using overload mechanism
}
You could simplify this for many types using fancy template metaprogamming.
Disclaimer: It's a long time since I've used union
s.... might miss something.