考虑以下(无效)代码示例:
// 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 = A
,TIn1
指定且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);
然后我必须略微改变呼叫签名,但那没关系。
答案 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;
}
编辑:在回答您的问题时,您还可以将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
};