简体   繁体   English

如何编写带有可变参数作为模板参数的成员函数

[英]How to write a member function with variadic parameters as template parameter

Is it possible to write template function in C++14 like below是否可以在 C++14 中编写模板函数,如下所示

Here is the sample https://godbolt.org/z/9gRk-t这是示例https://godbolt.org/z/9gRk-t

// pseudo code

#include <type_traits>

template <typename T, typename R, typename... Args>
decltype(auto) Call(T& obj, R(T::*mf)(Args...), Args&&... args) 
{
  return (obj.*mf)(std::forward<Args>(args)...);
}

So, for a test class所以,对于一个测试类

struct Test 
{
  int Func(){return 1;};
  bool Func(bool){return true;};  // overload

  void FuncInt(int){};
};

The template coudl work for the use case below (but it failed)模板 coudl 适用于下面的用例(但它失败了)

int main()
{
  Test test;

  // for overload case
  auto a = Call<int()>(test, &Test::Func);
  auto b = Call<bool(bool)>(test, &Test::Func, true);

  // for non-overload case
  Call(test, &Test::FuncInt, 1);

  return 0;
}

Herer the erro info.这是错误信息。

#1 with x64 msvc v19.24
example.cpp
<source>(23): error C2672: 'Call': no matching overloaded function found
<source>(23): error C2770: invalid explicit template argument(s) for 'decltype(auto) Call(T &,R (__cdecl T::* )(Args...),Args &&...)'
<source>(5): note: see declaration of 'Call'
<source>(24): error C2672: 'Call': no matching overloaded function found
<source>(24): error C2770: invalid explicit template argument(s) for 'decltype(auto) Call(T &,R (__cdecl T::* )(Args...),Args &&...)'
<source>(5): note: see declaration of 'Call'
Compiler returned: 2

Issue with问题与

template <typename T, typename R, typename... Args>
decltype(auto) Call(T& obj, R(T::*mf)(Args...), Args&&... args)

is that Args is deduced twice and should be identical.Args导出两次并且应该是相同的。

There are several ways to fix that issue:有几种方法可以解决该问题:

  • Add extra template parameter:添加额外的模板参数:

     template <typename T, typename R, typename... Args, typename ... Ts> decltype(auto) Call(T& obj, R(T::*mf)(Args...), Ts&&... args) { return (obj.*mf)(std::forward<Ts>(args)...); }

    Demo演示

  • or make parameter non deducible (I use std::type_identity from C++20 but can trivially be reimplemented in previous version):或使参数不可推导(我使用 C++20 中的std::type_identity但可以在以前的版本中轻松地重新实现):

     template <typename T, typename R, typename... Args> decltype(auto) Call(T& obj, R(T::*mf)(Args...), std::type_identity_t<Args>... args) { return (obj.*mf)(std::forward<Args>(args)...); }
  • or change completely signature:或完全更改签名:

     template <typename T, typename M, typename... Args> decltype(auto) Call(T& obj, M mf, Args&&... args) { return (obj.*mf)(std::forward<Args>(args)...); }

    Demo演示

In your declaration of Call :在您的Call声明中:

template <typename T, typename R, typename... Args>
decltype(auto) Call(T& obj, R(T::*mf)(Args...), Args&&... args);

the function template takes (or might try to deduce) two or more template arguments: the first is T , the second is R , and the rest are Args .函数模板采用(或可能尝试推导出)两个或多个模板参数:第一个是T ,第二个是R ,其余的是Args So giving a single function type as the first template argument as in Call<int()> and Call<bool(bool)> is wrong.因此,在Call<int()>Call<bool(bool)> ,将单个函数类型作为第一个模板参数是错误的。 The correct way to call it would be调用它的正确方法是

auto a = Call<Test, int>(test, &Test::Func);
auto b = Call<Test, bool, bool>(test, &Test::Func, true);

Another issue is that if you want the template arguments deduced, as in the non-overloaded case, since the Args pack appears twice, it will only work if the lists deduced from the member function and from the trailing arguments are exactly the same:另一个问题是,如果您想要推导出模板参数,就像在非重载的情况下一样,由于Args包出现两次,只有从成员函数和尾随参数推导出的列表完全相同时,它才会起作用:

int n = 3;
Call(test, &Test::FuncInt, n); // error!
// Args... is deduced to `int` from `&Test::FuncInt`, but deduced to `int&`
// from `n` since it's an lvalue matching a forwarding reference parameter.

If you prefer the function type syntax, you could use the solution of @foo :如果您更喜欢函数类型语法,则可以使用@foo的解决方案:

template <typename FuncT, typename T, typename... Args>
constexpr decltype(auto) Call(T& obj, FuncT T::*mf, Args&&... args)
    noexcept(noexcept((obj.*mf)(std::forward<Args>(args)...)))
{
    return (obj.*mf)(std::forward<Args>(args)...);
}

// main() exactly as in question, including Call<int()> and Call<bool(bool)>.

FuncT T::*mf is the syntax for declaring a pointer-to-member, which is often used to point to a data member, but also works to point to a function if the type FuncT is a function type. FuncT T::*mf是声明成员指针的语法,通常用于指向数据成员,但如果类型FuncT是函数类型,也可用于指向函数。 (I've added the constexpr and conditional exception specifier to make it more generic.) (我添加了constexpr和条件异常说明符以使其更通用。)

This also solves an issue with the original, which can't be used to invoke a member function which is const or which has a ref-qualifier, since this creates a different function type:这也解决了原始函数的一个问题,它不能用于调用const或具有 ref 限定符的成员函数,因为这会创建不同的函数类型:

class Test2 {
public:
    int get() const;
    void set() &;
};

void driver_Test2() {
    Test2 test;

    // Error with original Call:
    // Type of &Test2::get is "int (Test2::*)() const",
    // which cannot match "R (Test2::*)(Args...)"
    int a = Call(test, &Test2::get);

    // Error with original Call:
    // Type of &Test2::set is "void (Test2::*)(int) &",
    // which cannot match "R (Test2::*)(Args...)"
    Call(test, &Test2::set, 1);
}

But with the new Call definition, driver_Test2 is fine, since any non-static member function can match FuncT T::* .但是对于新的Call定义, driver_Test2很好,因为任何非静态成员函数都可以匹配FuncT T::* If we wanted to supply a template argument to the calls in driver_Test2 , maybe because the member functions are overloaded, that would look like Call<int() const> and Call<void() &> .如果我们想为driver_Test2的调用提供模板参数,可能是因为成员函数被重载了,它看起来像Call<int() const>Call<void() &>

I have figured it out.我已经想通了。 It is quietly like std::men_fn.它安静地像 std::men_fn。

Here it is the code below and sample https://godbolt.org/z/NoWPV_这是下面的代码和示例https://godbolt.org/z/NoWPV_


#include <type_traits>

template<typename T>
struct Proxy
{
    template<typename R, typename ...Args>
    decltype(auto) Call(T& obj, R T::*mf, Args&&... args)
    {
        return (obj.*mf)(std::forward<Args>(args)...);
    }
};

struct Test 
{
  int Func(){return 1;};
  bool Func(bool){return true;};  // overload

  void FuncInt(int){};
};

int main()
{
  Test test;
  Proxy<Test> proxy;

  // for overload case
  auto a = proxy.Call<int()>(test, &Test::Func);
  auto b = proxy.Call<bool(bool)>(test, &Test::Func, true);

  // for non-overload case
  proxy.Call(test, &Test::FuncInt, 1);

  return 0;
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

相关问题 如何将参数从可变参数模板成员函数存储到向量? - How to store parameters from variadic template member function into vector? 完美地将可变参数模板参数转发给成员函数 - Perfect forwarding variadic template parameters to member function 使用成员函数指针作为参数的可变参数模板 - Variadic template with member function pointer as parameter 如何编写以对象为参数的可变参数模板函数? - How to write a variadic-template function with objects as parameters? 使用不属于函数参数的可变参数模板参数调用成员函数 - Invoking a member function with variadic template parameters that are not part of the function's arguments 可变参数模板中的函数参数 - Function parameter in variadic template 可变参数成员函数模板是否可以具有两个参数的基本情况? - Is it possible to have a two parameter base case for a variadic member function template? C ++:具有可变参数模板的成员函数指针参数 - C++: Member function pointer parameter with variadic template 类型 std::function 的类成员内部的可变模板参数扩展 - Variadic Template Parameter Expansion Inside Class member of Type std::function 获取可变参数模板可变参数模板参数可变参数 - Get variadic template variadic template parameter variadic parameters
 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM