2

我想实现一个“任务”类,它可以存储一个函数指针以及一些要传递给它的参数。像 std::bind()。我对如何存储参数有一些疑问。

class BaseTask {
public:
    virtual ~BaseTask() {}
    virtual void run() = 0;
};

template<typename ... MTArg>
class Task : public BaseTask {
public:
    typedef void (*RawFunction)(MTArg...);

    Task(RawFunction rawFunction, MTArg&& ... args) : // Q1: MTArg&& ... args
        rawFunction(rawFunction),
        args(std::make_tuple(std::forward<MTArg>(args)...)) {} // Q2: std::make_tuple(std::forward<MTArg>(args)...)

    virtual void run() {
        callFunction(GenIndexSequence<sizeof...(MTArg)>()); // struct GenIndexSequence<count> : IndexSequence<0, ..., count-1>
    }
private:
    template<unsigned int... argIndexs>
    inline void callFunction() {
        rawFunction(std::forward<MTArg>(std::get<argIndexs>(args))...);
    }

private:
    RawFunction rawFunction;
    std::tuple<MTArg...> args; // Q3: std::tuple<MTArg...>
};

Q1:MTArg 之后是否需要 &&

Q2:这种方式对 init args 是否正确

Q3:args 的类型是否正确,我需要:

std::tuple<special_decay_t<MTArg>...>

根据这个:http ://en.cppreference.com/w/cpp/utility/tuple/make_tuple

// 问题结束

  1. 我希望 Task 可以这样使用:

    void fun(int i, const int& j) {}
    
    BaseTask* createTask1() {
        return new Task<int, const int&>(&fun, 1, 2); // "2" must not be disposed at the time of task.run(), so inside Task "2" should be store as "int", not "const int&"
    }
    
    BaseTask* createTask2(const int& i, const int& j) {
        return new Task<int, const int&>(&fun, i, j); // "j" must not be disposed at the time of task.run(), so inside Task "j" should be store as "int", not "const int&"
    }
    
    void test(){
        createTask1()->run();
        createTask2(1, 2)->run();
    }
    
  2. 任务只会运行一次,即零次或一次。

4

2 回答 2

1

Q1:MTArg 之后是否需要 &&
Task(RawFunction rawFunction, MTArg&& ... args) : // Q1: MTArg&& ... args

不,没有必要,因为它是一个具体的函数(在这种情况下是构造函数)。如果它是一个模板函数会很重要,那么 args 将是通用引用类型。

出于同样的原因,您不需要使用std::foward.

Q3:args 的类型是否正确,我需要:
std::tuple<special_decay_t<MTArg>...>

是的,因为类型不应该是右值,如果你想将它们存储在一个元组中。

于 2013-11-04T08:03:44.310 回答
1

std::tuple::special_decay_t即使它存在也不允许使用:它是一个实现细节。cppreference 上的代码仅用于说明:它“好像”存在该类型。如果您的实现使用该代码,那是不同编译器将没有的实现细节,并且您的编译器的下一次迭代可以自由更改/使私有/重命名/等。

作为说明,它解释了如果要重复特殊的衰减过程std::make_tuple以及其他一些 C++11 接口需要编写的内容。

作为第一步,我会保留你的整体设计,并稍微修复一下。然后我会指出一个替代方案。

MTArg...是函数的参数:

template<typename ... MTArg>
struct Task {
  typedef void (*RawFunction)(MTArg...);

在这里,我们想将一组参数转发到 atuple中,但参数不必匹配MTArg——它们只需要是可转换的:

  template<typename ... Args>
  explicit Task(RawFunction rawFunction, Args&&... args)
    rawFunction(rawFunction),
    args(std::make_tuple(std::forward<Args>(args)...)) {}

以上检查。注意我做了它explicit,好像Args...是空的,我们不希望它成为转换构造函数。

  void run() {
    callFunction(GenIndexSequence<sizeof...(MTArg)>());
  }
private:
  template<unsigned int... argIndexs>
  inline void callFunction() {
    rawFunction(std::forward<MTArg>(std::get<argIndexs>(args))...);
  }
  RawFunction rawFunction;
  std::tuple<MTArg...> args;
};

然后我们写一个make_task函数:

template<typename ... MTArg, typename... Args>
Task<MTArg...> make_task( void(*raw)(MTArg...), Args...&& args ) {
  return { raw, std::forward<Args>(args)... };
}

看起来像那样。请注意,我们MTArg...从函数指针和Args...参数中推断。

仅仅因为我们的函数foo按值取值,并不意味着我们应该复制它,或者存储对它的右值引用,或者你有什么。

现在,以上内容不同意如何std::functionstd::thread工作,这就是special_decay_t进来的地方。

通常,当您参数存储到函数时,您希望存储实际副本,而不是对可能会消失的局部变量或临时变量的引用。如果您的函数通过左值非常量引用获取参数,您需要在调用站点特别注意不要将其绑定到堆栈变量。

这就是reference_wrapper进来的地方和special_decay_t位:推导的参数被衰减为具有常规生命周期的文字。打包成std::reference_wrapper的类型变成了引用,这让你可以通过接口传递引用。

我很想复制 C++11 模式,但强制 a 的创建者Task明确reference_wrapper所有应该通过引用传递给原始函数的类型。

但我不确定,这样做的代码有点混乱。

于 2013-11-04T08:50:54.063 回答