通过组合实现函数对象重载

4

假设有一些现有的函数对象:

struct incr {
    int operator()(int x) const { return x + 1; }
};

struct rep_str {
    std::string operator()(const std::string& s) const { return s + s; }
};

我想知道是否有可能实现这样的功能:
auto f = overload<incr, rep_str>();
f(1);        // returns 2
f("hello");  // returns "hellohello"

多个重载函数可能如下所示:

auto f = overload<fa, fb, fc, ...>();
// or...
auto g = overload<fa, overload<fb, overload<fc, ...>>>();

我正在考虑使用SFINAE和std::result_of_t之类的东西,但还没有想清楚如何做。

这个语法中,3个重载的组合会如何工作?是 overload< X, overload< Y, Z > > 还是 overload< X, Y, Z > - Xeren Narcy
@XerenNarcy 我认为这两种设计都可以。overload<X, overload<Y, Z>> 对我来说看起来像是嵌套的对,而 overload<X, Y, Z> 则让我想起了元组(这样我们就可以做 overload_cat 或类似的事情)。 - Zizheng Tai
1
甚至有一个提案将类似的东西纳入标准。它还没有准备好用于C++17,但已经在C++20的轨道上了。还有一个[参考实现]。(https://github.com/viboes/tags/blob/master/include/yafpl/v1/functional/overload.hpp) - Fabio Fracassi
3个回答

8

你不需要过于精细:只需从所有参数中继承,并使用using-declarationsoperator()从基类引入。然而,在可变参数的情况下,您无法在using-declaration中进行包展开,因此必须采用递归方法,如下所示:

template <class... Ts>
struct overload {}; // only used for empty pack

template <class T>
struct overload<T> : private T {
    using T::operator();
};

template <class T1, class T2, class... Ts>
struct overload<T1, T2, Ts...> : private T1, overload<T2, Ts...> {
    using T1::operator();
    using overload<T2, Ts...>::operator();
};

哇,我真的没想到这是可行的。令人印象深刻。 - Zizheng Tai
1
更好的是,它有一个明确定义的解析顺序。 - Xeren Narcy

2
的答案更好,但我也参与了其中,这是我的版本:
#include <type_traits>
#include <utility>

template <typename... Fns>
struct overload;

template <typename Fn, typename... Fns>
struct overload<Fn, Fns...>
{
    template <typename... T>
    std::result_of_t<Fn(T...)> operator()(T && ... args) const {
        return Fn()(std::forward<T>(args)...);
    }

    using next = overload<Fns...>;

    template <typename... T>
    std::result_of_t<next(T...)> operator()(T && ... args) const {
        return next()(std::forward<T>(args)...);
    }
};

0

可以使用模板特化来实现:

#include <string>
#include <iostream>
template <typename...Args>
struct overload{

};
template <> struct overload<int>{
    int operator()(int x) const { return x + 1; }
};
template <> struct overload< std::string>{
    std::string operator()(const std::string& s) const { return s + s; }
};
template <typename...Args >
auto f(Args...arg){
    overload<Args...> func;
    return func(arg...);
}
int main()
{
    std::cout << f(3) << std::endl << f(std::string("Hello"));    
}

注意:@Brian和@md5i提供的两个答案更加通用、优雅、完美和更好。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接