C ++ 11,静态方法中派生类的类型推导

时间:2014-05-23 18:43:58

标签: c++ c++11 static-methods template-deduction

最小例子:

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);

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

3 个答案:

答案 0 :(得分:4)

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

你可以做些不同的事情,例如,使用CRTP(正如Massa在评论中所建议的那样)让派生类型将它注入基础类型 - 具有不同的成本不同基础的类型,如果你有一些实际的接口,你需要提供CRTP作为TaskMyTask之间的中间帮助器类型,或者更好地作为外部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

要在模板的上述代码中支持 nice 语法而不必提供模板参数,您可以根据模板实现make而不是类型

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

答案 1 :(得分: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.);

Live demo

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

答案 2 :(得分: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;
}