C++11:计算可变参数函数参数类型

发布于 2024-12-15 08:07:04 字数 1721 浏览 3 评论 0原文

假设您要编写一个函数,该函数将不透明句柄传递给未知类型的函数(例如,包含具有商定名称的函数的结构的名称),并将参数转发给该函数。

在非可变参数的情况下,为了简单起见,考虑单参数函数,有两种方法可以做到这一点:您可以让转发函数接受任意类型的参数,并尝试用它调用转发者函数,编译器会抱怨在模板扩展期间如果发现不兼容;或者您可以使用 decltype 和各种其他机制来确定转发者函数期望的参数类型,并明确要求该类型的参数。我不知道这些是否有任何公认的术语,所以我将它们称为“通过”和“预先”。

传递方法可以直接推广到具有任意数量参数的函数,但前面的方法则不然。

#include <iostream>

template<typename T, typename Arg>
void pass_through_1(Arg arg)
{
    T::f(arg);
}

template<typename T> struct arg_of_1;

template<typename Ret, typename Arg>
struct arg_of_1<Ret (Arg)>
{
    typedef Arg type;
};

template<typename T>
void up_front_1(typename arg_of_1<decltype(T::f)>::type arg)
{
    T::f(arg);
}

template<typename T, typename... Args>
void pass_through_var(Args... args)
{
    T::f(args...);
}

template<typename T> struct args_of_var;

template<typename...> struct type_list;

template<typename Ret, typename... Args>
struct args_of_var<Ret (Args...)>
{
    // typedef Args... type; // can't do this
    typedef type_list<Args...> type;
};

// template<typename T>
// void up_front_var(typename args_of_var<decltype(T::f)>::type... args) // can't do this
// {
//     T::f(args...);
// }

struct test  
{ 
    static void f(int x) { std::cout << x*9 << std::endl; }
};

int main(int, char**)
{
    pass_through_1<test>(7);
    up_front_1<test>(8);
    pass_through_var<test>(9);
    // up_front_var<test>(10);
    return 0;
}

问题是参数包不允许独立存在,只能作为模板参数,如果将它们包装在封闭的模板中,则无法将它们解开并解开,只能通过模式匹配。

“Up front”具有一些优点,例如更好的自文档以及对类型推断的更好支持(up_front本身可以被 decltyped)。有什么方法可以让它在可变的情况下工作吗? (您当然可以使用 std::tuple,但这相当不令人满意。)

Let's say you want to write a function which gets passed an opaque handle to a function of unknown type (say, the name of a struct containing a function with an agreed-upon name), and forwards arguments to that function.

In the non-variadic case, considering single-parameter functions for simplicity, there's two ways to do this: you can let the forwarding function take an argument of arbitrary type, and attempt to call the forwardee function with it, and the compiler will complain during template expansion if it turns out to be incompatible; or you can use decltype and assorted other mechanisms to figure out what type of parameter the forwardee function expects, and explicitly require an argument of that type. I don't know if there's any accepted terminology for these, so I'm going to call them "pass through" and "up front".

The pass through method generalizes straightforwardly to functions with an arbitrary number of parameters, but the up front method doesn't.

#include <iostream>

template<typename T, typename Arg>
void pass_through_1(Arg arg)
{
    T::f(arg);
}

template<typename T> struct arg_of_1;

template<typename Ret, typename Arg>
struct arg_of_1<Ret (Arg)>
{
    typedef Arg type;
};

template<typename T>
void up_front_1(typename arg_of_1<decltype(T::f)>::type arg)
{
    T::f(arg);
}

template<typename T, typename... Args>
void pass_through_var(Args... args)
{
    T::f(args...);
}

template<typename T> struct args_of_var;

template<typename...> struct type_list;

template<typename Ret, typename... Args>
struct args_of_var<Ret (Args...)>
{
    // typedef Args... type; // can't do this
    typedef type_list<Args...> type;
};

// template<typename T>
// void up_front_var(typename args_of_var<decltype(T::f)>::type... args) // can't do this
// {
//     T::f(args...);
// }

struct test  
{ 
    static void f(int x) { std::cout << x*9 << std::endl; }
};

int main(int, char**)
{
    pass_through_1<test>(7);
    up_front_1<test>(8);
    pass_through_var<test>(9);
    // up_front_var<test>(10);
    return 0;
}

The problem is that parameter packs aren't allowed to be free-standing, only as template arguments, and if you wrap them in an enclosing template, there's no way to unwrap-and-unpack them in place, only by pattern matching.

"Up front" has some advantages like better self-documentation, and better support for type inference (up_front<T> can itself be decltyped). Is there any way to make it work in the variadic case? (You could of course use std::tuple, but that's rather unsatisfying.)

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

青衫负雪 2024-12-22 08:07:09

没有什么比写下问题更能让你找到答案了。

这是一种方法:

template<typename T, typename Args = typename args_of_var<decltype(T::f)>::type>
struct up_front_var;

template<typename T, typename... Args>
struct up_front_var<T, type_list<Args...>>
{
    static void forward(Args... args)
    {
        T::f(args...);
    }
};

我认为没有办法从中创建一个顶级函数(您再次遇到原始问题),但这可能还不错。

仍然很高兴看到其他解决方案。

There's nothing like writing down the question to make you realize the answer.

Here's one way:

template<typename T, typename Args = typename args_of_var<decltype(T::f)>::type>
struct up_front_var;

template<typename T, typename... Args>
struct up_front_var<T, type_list<Args...>>
{
    static void forward(Args... args)
    {
        T::f(args...);
    }
};

I don't think there's a way to make a top-level function out of this (you run into the original problem again), but that's possibly not too bad.

Would still be happy to see other solutions.

临风闻羌笛 2024-12-22 08:07:09

也许我没有正确理解这个问题,但你总是可以省略参数类型并让编译器推断它们。

/* declare */
template<typename T, typename... Args>
void up_front_var(Args... args)
{
    T::f(std::forward<Args>(args)...); // need std::forward to correctly handle lvalue/rvalue references
}

/* an example */
class Test {
public:
    static void f(const char* fmt, int a, int b);
};

void Test::f(const char* fmt, int a, int b)
{
    printf(fmt, a, b); 
}


int main()
{
    up_front_var<Test>("testing %u, %u", 1, 2); // no need to specify arguments here
    return 0;
}

Maybe I'm not understanding the question correctly, but you can always leave out the argument types and let the compiler infer them.

/* declare */
template<typename T, typename... Args>
void up_front_var(Args... args)
{
    T::f(std::forward<Args>(args)...); // need std::forward to correctly handle lvalue/rvalue references
}

/* an example */
class Test {
public:
    static void f(const char* fmt, int a, int b);
};

void Test::f(const char* fmt, int a, int b)
{
    printf(fmt, a, b); 
}


int main()
{
    up_front_var<Test>("testing %u, %u", 1, 2); // no need to specify arguments here
    return 0;
}
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文