c++ - 构造函数重载以接受任何函数

标签 c++ templates c++17 constructor-overloading

我试图创建一个类模板,其构造函数可以接受任何类型的函数作为参数,也就是说,它接受一个函数指针(可以是成员函数指针)和相应的函数参数。此外,应该有一个static_assert来检查函数返回类型(从函数指针获取)是否与类模板参数类型匹配。因此,代码应如下所示:

template <class ReturnType>
struct Bar
{
    template <class RetType, class ... ParamType>
    Bar<ReturnType>(RetType (* func)(ParamType ...), ParamType && ... args) :
        package_(std::bind(func, std::forward<ParamType>(args) ...)),
        function_([this] { package_(); }),
        future_(package_.get_future())
    {
        static_assert(std::is_same<ReturnType, RetType>::value,
            "Type mismatch between class parameter type and constructor parameter type");
    }

    template <class RetType, class ObjType, class ... ParamType>
    Bar<ReturnType>(RetType (ObjType::* func)(ParamType ...), ObjType * obj, ParamType && ... args) :
        package_(std::bind(func, obj, std::forward<ParamType>(args) ...)),
        function_([this] { package_(); }),
        future_(package_.get_future())
    {
        static_assert(std::is_same<ReturnType, RetType>::value,
            "Type mismatch between class parameter type and constructor parameter type");
    }

    std::packaged_task<ReturnType()> package_;
    std::function<void()> function_;
    std::future<ReturnType> future_;
};

我们的想法是,代码针对这些情况进行编译,并允许(通过函数调用运算符)无错误地调用 Bar::function_:

struct Foo
{
    int foo(int i) {
        return i;
    }

    int foo() {
        return 1;
    }
};

int foo(int i)
{
    return i;
}

int foo()
{
    return 1;
}

int main()
{
    Foo f = Foo();

    Bar<int> b1(&Foo::foo, &f, 1);
    Bar<int> b2(&Foo::foo, &f);
    Bar<int> b3(foo, 1);
    Bar<int> b4(foo);

    return 0;
}

不幸的是,我对模板元编程的经验几乎为零,尽管我已经在 SO 中遇到了几个问题,并尝试了多种方法来解决我的问题,例如对构造函数使用更通用的方法

template <class RetType, class ... ParamType>
Bar<ReturnType>(RetType func, ParamType && ... args)

并将其与 type_traits 结合起来以确定返回类型),我还没有找到一种方法来完成这项工作。我可以对允许此功能的构造函数进行哪些更改?

编辑:

max66的回答解决了我原来的问题,但是,出现了一个新的问题,这是我在上一个问题中没有考虑到的。我还希望能够将变量传递给构造函数,如下所示:

int main()
{
    Foo f = Foo();
    int i = 1;

    Bar<int> b1(&Foo::foo, &f, i); // Error
    Bar<int> b2(&Foo::foo, &f, 1); // Ok
    Bar<int> b3(&Foo::foo, &f); // Ok
    Bar<int> b4(foo, i); // Error
    Bar<int> b5(foo, 1); // Ok
    Bar<int> b6(foo); // Ok

    return 0;
}

然而,事实上,在标有Error的情况下会出现编译器错误。我猜测这是因为构造函数中的参数 func 使用 ParamType 来确定其类型(与实际的 ParamType 不匹配)在 b1b4 的情况下),但我不知道如何解决这个问题......

最佳答案

您可能想使用 std::invoke 。它为您处理成员函数指针和常规函数。

作为您可以做的事情的概述:

#include <functional>
#include <type_traits>
#include <utility>

template<typename F>
class Bar
{
    F f_;

public:
    template<typename TF>
    Bar(TF && f)
        : f_{ std::forward<TF>(f) }
    {}

    template<typename... Args>
    decltype(auto) operator()(Args &&... args) {
        return std::invoke(f_, std::forward<Args>(args)...);
    }
};

template<typename F>
auto make_bar(F && f)
{
    return Bar<std::decay_t<F>>{ std::forward<F>(f) };
}

可以像这样使用:

auto b1 = make_bar(&f);
auto result = b1(myArg1, myArg2); // etc

auto b2 = make_bar(&Foo::fn);
auto result = b1(foo, arg1);

至少,我建议让 Bar 将函数对象类型作为模板参数,这样您就不必使用 std::function,但如果您确实想使用现有的确切调用语法,也可以使用 std::invokestd::invoke_result 来完成。

关于c++ - 构造函数重载以接受任何函数,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/44357883/

相关文章:

javascript - 如何将参数从 Tornado 传递到 js 文件而不是 html?

c++ - 在编译时创建所有可能的键值类型映射的变体类型,其中键和值类型是从类型元组中指定的

c++ - 模板实例化导致函数膨胀

c++ - 为什么 make_optional 不适用于文件流?

c++ - 二维正态分布 C++

c++ - 模板 + Typedef C++

c++ - 从函数返回 const char* 的正确方法,例如覆盖 std::exception::what()

c++ - 超出范围的枚举的整数初始化

c++ - C++20 中的 std::launder 用例

c++ - 单例日志类中运算符 << 的问题