具有不同参数数量的重载的函数模板特化

时间:2014-04-09 14:49:30

标签: c++ templates c++11 template-specialization

考虑以下(无效)代码示例:

// a: base template for function with only one parameter
template<typename T>
void f(T t) { }

// b: base tempalte for function with two parameters
template<typename T1, typename T2>
void f(T1 t1, T2 t2) { }

// c: specialization of a for T = int
template<>
void f<int>(int i) { }

// d: specialization for b with T1 = int - INVALID
template<typename T2>
void f<int, T2>(int i, T2 t2) { }

int main() {
    f(true);        // should call a
    f(true, false); // should call b

    f(1);           // should call c
    f(1, false);    // should call d
}

我已经阅读this walk-through了解为什么一般来说,部分功能模板专业化不会起作用,我认为我理解基本推理:有些情况下功能模板专业化和重载会使某些调用含糊不清(文章中有很好的例子)。

然而,这个具体的例子是否有效的原因除了&#34;标准说它不应该&#34;&#34;?如果我可以保证(例如使用static_assert)基本模板从未实例化,是否会发生任何变化?有没有其他方法可以达到同样的效果?


我真正想要实现的是创建一个可扩展的工厂方法

template<typename T>
T create();

也有一些带输入参数的重载,例如

template<typename T, typename TIn>
T create(TIn in);
template<typename T, typename TIn1, typename TIn2>
T create(TIn1 in1, TIn2 in2);

为了确保存在所有必需的工厂方法,我在函数库模板中使用static_assert,这样如果使用模板参数调用create方法时会生成编译器错误,而没有专门化的参数提供。

我希望这些是函数模板而不是类模板,因为它们会有很多,并且它们都将使用来自相同结构层次结构的输入,因此实例化10个工厂而不是一个工厂会带来一些开销,我和# 39;我想避免(不考虑代码变得更容易理解的事实,如果我可以让它工作......)。

有没有办法解决这篇文章上半部分中提到的问题,以实现我在下半年尝试实现的目标?


回应iavr

我可以通过简单重载执行此操作,这将(​​如上面的模板)提供类似

的内容
template<typename TIn2>
A create(bool, TIn2);
template<typename TIn2>
A create(int, TIn2);

如果我需要两个不同的部分专精,T = ATIn1指定且TIn2仍然未指定。这是一个问题,因为我有一些情况(实际上是元编程和模板的文本书案例)我知道,例如,其中一个参数将是std::string,另一个将属于某种类型,具有属性fields和属性grids,分别属于std::vector<field>std::vector<grid>类型。我不知道将作为第二个参数提供的所有类型 - 我确信它们会比我目前实现的更多 - 但是方法的实现将完全相同

在撰写此更新时,我认为我已经找到了重新设计实现的方法,因此不需要部分专业化 - 基本上,我执行以下操作来涵盖上述情况:

template<>
A create<A, std::vector<field>, std::vector<grid>>(std::vector<field> fs, std::vector<grid> gs);

然后我必须略微改变呼叫签名,但那没关系。

3 个答案:

答案 0 :(得分:2)

我分享您的担忧,也许在这种特殊情况下,使用功能模板部分特化没有问题,但是再一次,就是这样,那么使用普通重载会有什么问题呢?

// a: base template for function with only one parameter
template<typename T>
void f(T t) { }

// b: base template for function with two parameters
template<typename T1, typename T2>
void f(T1 t1, T2 t2) { }

// c: specialization of a for T = int
void f(int i) { }

// d: specialization for b with T1 = int
template<typename T2>
void f(int i, T2 t2) { }

这也减少了输入,我知道这就是你不想使用函数对象的原因(这会有部分特殊化)。

答案 1 :(得分:2)

以下是使用类模板特化的简单解决方法:

template <typename, typename...>
struct Creator;

template <typename T, typename TIn>
struct Creator<T, TIn>
{
    T call(TIn in)
    {
        // ...
    }
};

template<typename T, typename TIn1, typename TIn2>
struct Creator<T, TIn1, TIn2>
{
    T call(TIn1 in1, TIn2 in2)
    {
        // ...
    }
};

template <typename R, typename... Arguments>
R Create(Arguments&&... arguments)
{
    return Creator<R, Arguments...>::call(std::forward<Arguments>(arguments)...);
}

答案 2 :(得分:1)

如果您不想重载,并希望能够从单独的文件中进行专业化,那么我认为您应该根据您问题的链接上的解决方案。它涉及在您专门化的类上创建静态方法。从我对这个问题的解读来看,你只对专注于T感兴趣,而不是对你打算转发的论证数量感兴趣。在C ++ 11中,您可以执行以下操作:

#include <iostream>
#include <utility>
using namespace std;


template<typename T>
struct factory_impl;
// Left unspecified for now (which causes compliation failure if
// not later specialized

template<typename T, typename... Args>
T create(Args&&... args)
{
   return factory_impl<T>::create(std::forward<Args>(args)...);
}


// Note, this can be specified in a header in another translation
// unit. The only requirement is that the specialization
// be defined prior to calling create with the correct value
// of T
template<>
struct factory_impl<int>
{
   // int can be constructed with 0 arguments or 1 argument
   static int create(int src = 0)
   {
     return src;
   }
};

int main(int argc, char** argv)
{
   int i = create<int>();
   int j = create<int>(5);
   // double d = create<double>(); // Fails to compile

   std::cout << i << " " << j << std::endl;
   return 0;
}

实例http://ideone.com/7a3uRZ

编辑:在回答您的问题时,您还可以将create作为班级的成员函数,并通过调用传递部分数据或在

之前或之后执行操作
struct MyFactory
{

   template<typename T, typename... Args>
   T create(Args&&... args)
   {
      T ret = factory_impl<T>::create(data, std::forward<Args>(args)...);
      // do something with ret
      return ret;
   }

   Foo data; // Example
};