如何声明一个具有 N 个 double 类型参数的函数?

Ada*_*ski 7 c++ template-meta-programming

我想要一个能够存储接受“N”个双参数的功能对象的类模板。此伪代码使用一个不存在的std::repeated_type函数模板来解决问题并说明预期用法:

template<int N>
class FunctionHolder {
public:
   using function_type = std::function<int(std::repeated_type<double, N> args)>;
   FunctionHolder(const function_type& arg): m_func(arg) {}
private:
   const function_type& m_func;
};

int my_func(double arg1, double arg2);

void main() {
   FunctionHolder<2> my_holder(my_func);  
}
Run Code Online (Sandbox Code Playgroud)

我希望代码尽可能简单且可读,因此即使我模糊地理解我可以使用 std::integer_sequence 和辅助类模板来缝合解决方案,但我不相信我的解决方案足够简单。

bit*_*ask 10

您可以递归地将函数参数添加到模板列表中:

template <typename Ret, std::size_t N, typename Type, typename... T>
struct RepeatFunctionArgType {
    static auto fnt() {
        if constexpr (sizeof...(T) >= N) {
            return std::function<Ret(T...)>{};
        } else {
            return RepeatFunctionArgType<Ret, N, Type, Type, T...>::fnt();
        }
    }
    using type = decltype(fnt());
};
Run Code Online (Sandbox Code Playgroud)

这可以作为 访问RepeatFunctionArgType<int,3,double>::type

static int foo(double,double,double) {return 0;}

int main() {
    RepeatFunctionArgType<int,3,double>::type fn = foo;
}
Run Code Online (Sandbox Code Playgroud)


Ted*_*gmo 9

您可以在未计算的上下文中使用 lambda 来获取其返回类型:

\n
using function_type =\n    decltype([]<std::size_t... I>(std::index_sequence<I...>) {\n        // expand to as many `double`s as there are `I`s:\n        return std::function<int(decltype((static_cast<void>(I)), double{})...)>{};\n    }(std::make_index_sequence<N>{}));\n
Run Code Online (Sandbox Code Playgroud)\n

或者

\n
using function_type =\n    decltype([]<std::size_t... I>(std::index_sequence<I...>)\n        // expand to as many `double`s as there are `I`s:\n        -> std::function<int(decltype((static_cast<void>(I)), double{})...)> {\n            // this space intentionally left blank\n        }(std::make_index_sequence<N>{}));\n
Run Code Online (Sandbox Code Playgroud)\n
\n

根据注释中的 \xe5\xba\xb7\xe6\xa1\x93\xe7\x91\x8b\ 代码改编的更通用的解决方案可能如下所示:

\n
template <std::size_t, class T>\nusing identity = T;\n\ntemplate <class R, class Arg, std::size_t N>\nstruct repeated_arg_func {\n    using type =\n        typename decltype([]<std::size_t... Is>(std::index_sequence<Is...>)\n                              -> std::type_identity<R(identity<Is, Arg>...)> {\n        }(std::make_index_sequence<N>{}))::type;\n};\n\ntemplate <class R, class Arg, std::size_t N>\nusing repeated_arg_func_t = typename repeated_arg_func<R, Arg, N>::type;\n\ntemplate <int N, class ArgType, class RetType>\nclass FunctionHolder {\npublic:\n    using signature = repeated_arg_func_t<RetType, ArgType, N>;\n    using function_type = std::function<signature>;\n    FunctionHolder(const function_type& arg) : m_func(arg) {}\n\nprivate:\n    function_type m_func;\n};\n
Run Code Online (Sandbox Code Playgroud)\n
\n

旁注:该const function_type&成员不是一个好主意,除非您实际上提供了一个std::function<...>比您的FunctionHolder. 您的示例创建了一个临时对象std::function<...>,该临时对象将在构建FunctionHolder完成后立即过期。

\n

我建议将其设为常规非参考成员:

\n
function_type m_func;\n
Run Code Online (Sandbox Code Playgroud)\n


Jar*_*d42 6

使用辅助类模板,可能是

template <std::size_t, typename T>
using always_t = T;

template<typename Seq> class FunctionHolderImpl;

template<std::size_t... Is>
class FunctionHolderImpl<std::index_sequence<Is...>> {
public:
   using function_type = std::function<int(always_t<Is, double>... args)>;

   FunctionHolder(const function_type& arg): m_func(arg) {}
private:
   function_type m_func;
};

template <std::size_t N>
using FunctionHolder = FunctionHolderImpl<std::make_index_sequence<N>>;
Run Code Online (Sandbox Code Playgroud)


P K*_*mer 5

这是一个 C++17 解决方案,您不必在 main() 中显式专门化持有者,也不必显式声明参数的数量。

#include <type_traits>
#include <functional>
#include <iostream>

// Some helpers to check if all arguments of the function have the same argument type
template<typename arg_t, typename... args_t>
static constexpr bool all_same_as_first()
{
    return std::conjunction_v<std::is_same<arg_t, args_t>...>;
};

template<typename... args_t>
static constexpr bool all_same()
{
    // Specialized logic for 0 or 1 argument, consider them the same. 
    if constexpr (sizeof...(args_t) < 2ul)
    {
        return true;
    }
    else
    {
        return all_same_as_first<args_t...>();
    }
}

// The actual function holder
// it also needs specialization on the return value type
template<typename retval_t, typename... args_t>
class FunctionHolder
{
public:
    static_assert(all_same<args_t...>()); // check condition that all arguments are of same type

    // construct
    explicit FunctionHolder(std::function<retval_t(args_t...)> fn) :
        m_fn{ fn }
    {
    }

    // allow calls to be made through the holder directly
    retval_t operator()(args_t&&...args)
    {
        return m_fn(std::forward<args_t>(args)...);
    }

private:
    std::function<retval_t(args_t...)> m_fn;
};

// helper for type deduction of std::function type 
template<typename fn_t>
auto make_holder(fn_t fn)
{
    std::function std_fn{fn};
    return FunctionHolder{ std_fn };
}

// your function
int my_func(double arg1, double arg2)
{
    return static_cast<int>(arg1+arg2);
}

// And the usage
int main()
{
    auto holder = make_holder(my_func);
    auto retval = holder(1.0, 2.0);

    return 0;
}
Run Code Online (Sandbox Code Playgroud)