Skip to content

Instantly share code, notes, and snippets.

@gintenlabo
Created August 28, 2013 08:06
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save gintenlabo/6363346 to your computer and use it in GitHub Desktop.
Save gintenlabo/6363346 to your computer and use it in GitHub Desktop.
compose overloaded function in C++11
#include <type_traits>
#include <utility>
namespace etude {
template<class... Fs>
struct overloaded_function_impl_;
template<>
struct overloaded_function_impl_<> {
template<class... Args,
typename std::enable_if<
sizeof...(Args) < 0 // always false
>::type* = nullptr
>
void operator()(Args...) const = delete;
};
template<class F, class = void>
struct function_holder_;
template<class F>
struct function_holder_<F,
typename std::enable_if<std::is_class<F>{}>::type>
: private F {
using F::operator();
template<class F_>
explicit function_holder_(F_ && f)
: F(std::forward<F_>(f)) {
}
};
template<class R, class... Args>
struct function_holder_<R (*)(Args...)> {
explicit function_holder_(R (*f)(Args...))
: f_(f) {
}
// should not be template
R operator()(Args... args) const {
return (*f_)(std::forward<Args>(args)...);
}
private:
R (*f_)(Args...);
};
template<class F, class... Fs>
struct overloaded_function_impl_<F, Fs...>
: private function_holder_<F>,
private overloaded_function_impl_<Fs...> {
typedef function_holder_<F> base1;
typedef overloaded_function_impl_<Fs...> base2;
using base1::operator();
using base2::operator();
template<class F_, class... Fs_>
overloaded_function_impl_(F_ && f, Fs_&&... fs)
: base1(std::forward<F_>(f)), base2(std::forward<Fs_>(fs)...) {
}
};
template<class... Fs>
class overloaded_function {
typedef overloaded_function_impl_<Fs...> impl_t;
public:
template<class... Fs_,
typename std::enable_if<
sizeof...(Fs) == sizeof...(Fs_)
>::type* = nullptr
>
overloaded_function(Fs_&&... fs)
: impl_(std::forward<Fs_>(fs)...) {
}
template<class... Args>
auto operator()(Args&&... args)
-> decltype(std::declval<impl_t&>()(std::forward<Args>(args)...)) {
return impl_(std::forward<Args>(args)...);
}
template<class... Args>
auto operator()(Args&&... args) const
-> decltype(std::declval<impl_t const&>()(std::forward<Args>(args)...)) {
return impl_(std::forward<Args>(args)...);
}
private:
overloaded_function_impl_<Fs...> impl_;
};
template<class... Fs>
auto make_overloaded(Fs... fs)
-> overloaded_function<Fs...> {
return {std::forward<Fs>(fs)...};
}
} // namespace etude
#include <iostream>
std::string hoge(char const*) {
return "char const*";
};
int main() {
auto f = etude::make_overloaded(
[] (int) -> std::string {
return "int";
},
[] (double) -> std::string {
return "double";
},
[] (void*) -> std::string {
return "void*";
},
hoge
);
std::cout << f(0) << std::endl;
std::cout << f(1.1) << std::endl;
std::cout << f(&f) << std::endl;
std::cout << f("hoge") << std::endl;
}
@lichray
Copy link

lichray commented Feb 26, 2014

I love this, which can simplify the use of boost::variant a lot.

Some tiny little problems:

  1. Some function objects may not be derivable, use __is_final;
  2. This does not handle member function pointers. It might make sense to copy INVOKE's capability;
  3. You intend to forward the function objects, but line 95 is not perfect forwarding;
  4. Why we need overloaded_function? overloaded_function_impl just works fine.

I recommend you to propose this to the C++ standard. We can discuss your ideas and drafts here:

https://groups.google.com/a/isocpp.org/forum/#!forum/std-proposals

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment