Metaprogramming pattern:
template<template<class...>class Z, class always_void, class...Ts> struct can_apply_helper:std::false_type{}; template<template<class...>class Z, class...Ts> struct can_apply_helper<Z, decltype((void)(Z<Ts...>)), Ts...>:std::true_type{}; template<template<class...>class Z, class...Ts> using can_apply=can_apply_helper<Z,void,Ts...>;
A value that determines whether the type expression will be a valid call:
// result_of_t fails to be SFINAE in too many compilers: template<class F, class...Ts> using invoke_helper_t=decltype( std::declval<F>()(std::declval<Ts>()...) ); template<class Sig> struct can_invoke; template<class F, class...Ts> struct can_invoke<F(Ts...)>: can_apply<invoke_helper_t, F, Ts...> {};
Now replace doStuff in your class. The first determines whether the function can be called using std::string const* :
template<class F, class=std::enable_if_t< can_invoke< F&(std::string const*) >{} >> void doStuff(F&& func) const { cout << "Const method called" << endl; for(const auto& i_string : m_vec) func(i_string); }
This one detects if you cannot call it with std::string const* and that you can call it std::string* :
template<class F, class=std::enable_if_t< !can_invoke< F&(std::string const*) >{} && can_invoke< F&(std::string*) >{} >> void doStuff(F&& func) { cout << "Non-const method called" << endl; doStuff([&func](const string *str) { auto mutableString = const_cast<string *>(str); func(mutableString); }); }
this also removes the unnecessary erasure of type std::function in your example and directs any call that can go to the const method to the const method.
Aside, std::vector<std::string*> storage is almost always bad.