指向成员函数的指针作为模板参数问题

问题描述 投票:0回答:1

我希望能够推迟对某个对象的成员函数的调用。因此,我需要能够将成员函数指针和参数存储为数据。 我的方法使用 C++ 模板,将成员函数指针视为类型的一部分。函数指针应该是模板参数,因为我希望能够在编译时通过

static_assert
选择允许哪些成员函数(即仅setter);

我正在使用

gcc (GCC) 14.1.1 20240507
C++ 23

示例代码:

struct Object {
    void set();
    void set(int test);
    int get();
};

// Utils:

template <typename Function>
struct FunctionTraits;

template <
    typename Return,
    typename... Args
> struct FunctionTraits
<Return (Object::*)(Args...)>
{
    using args_tuple_t = std::tuple<Args...>;
    using ret_t = Return;
};

// The member function wrapper:

template <
    typename Function,
    Function function
>
struct SetterTask
{
    Object* obj;
    typename FunctionTraits<decltype(function)>::args_tuple_t args;
};

// pseudo constructor

template <
    typename Return,
    typename... Args
>
auto makeSetter(
        Object* obj,
        Return (Object::*setter)(Args...),
        Args... args
)
{
    auto tuple = std::tuple( args... );
    using SetterType = Return (Object::*)(Args...);  // <-- ERROR
    return SetterTask<SetterType,setter>{            // <-- ERROR
        .obj = obj,
        .args = tuple
    };
}

template <
    typename Function,
    Function function
>
auto run(
        SetterTask<Function, function> setter
)
{
    return std::apply(setter.function,
            std::tuple_cat(
                std::make_tuple(setter.obj),
                setter.args
            )
    );
}

测试程序:

void test() {
    void (Object::*pFunc)(int) = &Object::set;
    Object obj;
    auto setter = makeSetter( &obj, pFunc, 10 );
    run(setter);
}

这是我得到的错误:

template_test.cpp:10:26:   required from here
   10 |         auto setter = makeSetter( &obj, pFunc, 10 );
      |                       ~~~~~~~~~~^~~~~~~~~~~~~~~~~~~
template_test.h:61:9: error: ‘setter’ is not a valid template argument for type ‘void (Object::*)(int)’
   61 |         };
      |         ^
template_test.h:61:9: note: it must be a pointer-to-member of the form ‘&X::Y’

我做错了什么?据我了解,类型都很明确,甚至过于明确。 或者这是一个编译器错误?

这是关键部分:

template <
    typename Return,
    typename... Args
>
auto makeSetter(
        Object* obj,
        Return (Object::*setter)(Args...),
        Args... args
)
{
    auto tuple = std::tuple( args... );
    using SetterType = Return (Object::*)(Args...);  // <-- ERROR
    return SetterTask<SetterType,setter>{            // <-- ERROR
        .obj = obj,
        .args = tuple
    };
}

我也尝试过:

    using SetterType = decltype(setter);  <-- decltype of pointer
    return SetterTask<SetterType,setter>{
            ...
    };

...还有...

    using SetterType = Return (Object::*)(Args...);
    return SetterTask<SetterType,(SetterType)setter>{ <-- explicitly cast pointer
            ...
    };
c++ templates function-pointers pointer-to-member c++23
1个回答
0
投票

正如 PiotrNycz 正确指出的那样,模板“值”参数不能从函数参数设置,也不能从任何运行时值设置。 非常明显,如果您考虑一下 - 模板参数是编译时常量。

这是我的解决方案:

template_test.h:

#include <tuple>
#include <type_traits>


struct Object {
    double set();
    void set(int test);
    void set2(int test);
    int get();
};

// Allowed member functions:

template <typename Function, Function function>
struct IsAllowedFunction : std::false_type {};

template <> struct IsAllowedFunction
<double (Object::*)(), (double (Object::*)())&Object::set>
: std::true_type {};

template <> struct IsAllowedFunction
<void (Object::*)(int), (void (Object::*)(int))&Object::set>
: std::true_type {};

template <> struct IsAllowedFunction
<void (Object::*)(int), (void (Object::*)(int))&Object::set2>
: std::true_type {};

// Utils:

template <typename Function>
struct FunctionTraits;

template <
    typename Return,
    typename... Args
> struct FunctionTraits
<Return (Object::*)(Args...)>
{
    using args_tuple_t = std::tuple<Args...>;
    using ret_t = Return;
};

// The member function wrapper:

template <
    typename Function,
    Function function
>
struct SetterTask
{
    Object* obj;
    typename FunctionTraits<Function>::args_tuple_t args;
};

// pseudo constructor

template <
    typename Function,
    Function function,
    typename... Args
>
auto makeSetter(
        Object* obj,
        Args... args
)
{
    static_assert(
            IsAllowedFunction<Function,function>::value,
            "Invalid function. Valid functions functionare: 'set', 'set2'"
    );
    auto tuple = std::tuple( args... );
    return SetterTask<Function,function>{
        .obj = obj,
        .args = tuple
    };
}

template <
    typename Function,
    Function function
>
auto run(
        SetterTask<Function, function> setter
)
{
    return std::apply(
            function,
            std::tuple_cat(
                std::make_tuple(setter.obj),
                setter.args
            )
    );
}

#define MAKE_SETTER(obj, Type, Function, ... ) \
    makeSetter<Type, (Type )&Object::Function>( obj, ## __VA_ARGS__ )

main.cpp:

#include "template_test.h"
#include <iostream>


void test() {
    Object obj;
    // Example 1: `double set()`:
    {
        auto setter = MAKE_SETTER(&obj,double (Object::*)(),set);
        double ret = run(setter);
        std::cout << "function returned: " << ret;
    }
    // Example 2: `void set(int)`:
    {
        auto setter = MAKE_SETTER(&obj,void (Object::*)(int),set, 10);
        run(setter);
    }
    // Example 3: `void set(int)`:
    {
        auto setter = MAKE_SETTER(&obj,void (Object::*)(int),set2, 10);
        run(setter);
    }
    // the following is not allowed
    // and raises a compile-time error:
    // auto getter = MAKE_SETTER(&obj,double (Object::*)(),get);
}

© www.soinside.com 2019 - 2024. All rights reserved.