0

最小的例子:

class Task
{
public:
  template<typename T, typename... Args>
  static T* make(Args... args) {
    return new T(args...);
  }
}

template<typename A, typename B, typename C>
class MyTask : public Task
{
public:
  MyTask(A a, B b, C c)
  {
  }
}

工厂方法的make存在使我不必在实例化模板化派生类时提供所有模板类型。

我希望能够尽可能简洁地创建 MyTask 的实例,即:

auto my_task = MyTask::make(a, b, c);

但是,编译器坚持认为它不能推导出 T,而是想要:

auto my_task = MyTask::make<MyTask<A, B, C>>(a, b, c);

这不是一个巨大的交易破坏者,但重复似乎是不必要的。有没有办法按照我想要的方式得到它?

4

3 回答 3

4

问题MyTask::make不是真的那样,而是Task::make。您在调用中使用的名称是查找的起点,但不是函数名称。所以技术上没有重复。

您可以做不同的事情,例如,使用 CRTP(如 Massa 在评论中建议的那样)让派生类型将其类型注入基础 - 如果您为不同的基础拥有不同的类型,则代价是有一些实际的接口,您需要提供 CRTP 作为Taskand之间的中间帮助器类型MyTask,或者更好地作为外部 CRTP 帮助器

template <typename T>
struct Maker {
    template <typename ...Args>
    static T *make(Args ... args) { return new T(args...); }
};

class MyTask : public Maker<MyTask>, public Task { ... };

auto task = MyTask::make(a,b,c);

另一种选择是使函数成为自由函数并仅传递您要构建的类型:

template <typename T, typename ...Args>
T* make(Args ... args);

auto x = make<MyTask>(a,b,c); // only named once here

为了在不提供模板参数的情况下支持上述模板代码中的良好make语法,您可以根据模板而不是类型来实现:

template <template <typename...> class T,
          typename ... Args>
T<Args...>* make(Args... args) {
    return new T<Args...>(args...);
}
于 2014-05-23T19:02:30.297 回答
2

由于几个不同的原因,您的问题没有意义 -make()是 的成员Task,但您谈论的是MyTask::make();你打算MyTask从中派生Task吗?也在这里

auto my_task = MyTask::make<MyTask>(a, b, c);
//                  ^^^          ^^^

显然你不能在MyTask不指定模板参数的情况下使用。

我认为您试图展示的是这个(我添加了完美转发):

template<typename T>
class Task
{
public:
  template<typename... Args>
  static T* make(Args&&... args) {
    return new T(std::forward<Args>(args)...);
  }
};

template<typename A, typename B, typename C>
class MyTask : public Task<MyTask<A, B, C>>
{
    public: MyTask(A, B, C) {}
};

你会用它作为

auto my_task = MyTask<int, long, double>::make(10, 20L, 30.);

这很冗长。这可以通过创建一个委托给Task::make()(或摆脱中间人并Task::make在此包装器本身内完成的工作,如果可行的话)的包装器函数来避免

template<typename A, typename B, typename C>
MyTask<A, B, C> *make_MyTask(A&& a, B&& b, C&& c)
{
    return Task<MyTask<A, B, C>>::make(std::forward<A>(a), 
                                       std::forward<B>(b), 
                                       std::forward<C>(c));
}

预期用途:

auto my_task = make_MyTask(10, 20L, 30.);

现场演示

另一条建议是您将make()函数更改为返回unique_ptr<...>而不是原始指针。

于 2014-05-23T19:10:28.257 回答
1

辅助函数将是有价值的;然后模板参数推导可以通过它发生

using namespace std;

template<typename T>
class Task
{
public:
    template<typename... Args>
    static T* make(Args... args) 
    {
        return new T(args...);
    }
};

// I find it easier to inherit from a class that actually knows what type to  
// return in the "make" function
template<typename A, typename B, typename C>
class MyTask : public Task<MyTask<A, B, C>>
{
public:
    MyTask(A a, B b, C c) { }
};

// basically this is the function, whose template argument deduction
// eliminates the need to manually specify the angle brackets
template<typename A, typename B, typename C>
MyTask<A, B, C>* MakeTask(A const& a, B const& b, C const& c) {
    return MyTask<A, B, C>::make(a, b, c);
}

int main() {
    // now usage only needs the function parameters. This scales to variadic args
    auto my_task = MakeTask(1, 2, 3);

    return 0;
}
于 2014-05-23T19:04:40.280 回答