基于c++模板参数的编译
c++ templates argument based compilation
我想写一个模板函数,根据模板参数的类型调用不同的方法。除
template<typename T>
void GetData(T& data)
{
if(T is int)
Call_Int_Method(data);
else if(T is double)
Call_Double_Method(data); // This call will not compile if data is int
...
}
基本上,GetData应该只调用基于t类型的一个方法,其他调用不应该存在。这有可能吗?
如果您需要整个函数对int
或double
具有特定行为,则提供GetData
的一些重载:
void GetData(int& data)
{
Call_Int_Method(data);
}
void GetData(double& data)
{
Call_Double_Method(data);
}
或Call_X_Methods
,如果只有一部分函数的逻辑要专门化:
template<typename T>
void DoTypeSpecificStuff(T& data) { /* stuff */ }
void DoTypeSpecificStuff(int& data) { .... }
void DoTypeSpecificStuff(double& data) { .... }
template<typename T>
void GetData(T& data)
{
DoTypeSpecificStuff(data);
// do other stuff
....
}
请注意,当类型匹配时,重载解析将优先考虑非模板而不是函数模板,因此您可以确保为这两种类型调用正确的函数。
既然还没有人把它放在这里,我想我应该给出另一个选项(这可能是多余的,简单的专门化/重载更具可读性)。
你可以在c++ 11(或boost(或你自己的特殊类)中使用SFINAE(替换失败不是错误),如果你想要做的是选择满足特定标准的类型。而你的例子是,如果T
是一个int
或T
是一个double
,如果我想使它比那更一般呢?我想选择如果T
是任何整型,如果T
是任何浮点型?
#include <string>
#include <iostream>
#include <type_traits>
template <typename T>
typename std::enable_if<std::is_floating_point<T>::value,T>::type foo(T val)
{
std::cout << "floating function" << std::endl;
return val;
}
template <typename T>
typename std::enable_if<std::is_integral<T>::value,T>::type foo(T val)
{
std::cout << "integral function" << std::endl;
return val;
}
int main(int argc,char** argv)
{
foo(2.0);//T==double, floating point function
foo(2);///T==int, integral function
foo(2.0f);//T==float, floating point function
//foo<std::string>(); throws no matching function for call to foo()
}
这是怎么回事?std::enable_if是一个只有type
和val==true
成员的模板。这使得std::enable_if<false,T>::type
是一个无效的语句。编译器不会自动将其作为错误抛出。相反,它会寻找它可以做的其他事情。
std::is_floating_point
和std::is_integral
只返回该类型的trait值。
老实说,我喜欢这比简单的专门化好一点,因为它更容易扩展:现在已经为unit8_t
, long double
, float
等编写了函数。你仍然可以在此基础上进行专门化,如果其中任何一个需要有所不同的话。与重载相比,您可以更容易地显式选择要讨论的函数(如果您想获得函数指针,&foo<int>
比(int(*)(int))&foo
更容易,更具可读性)。像所有模板方法一样,它是一个编译时操作,所以它不应该对运行时产生负面影响。
的缺点吗?好了,函数声明现在有99个字符长…
重载可能是这里的首选解决方案,更不用说因为它们给您带来了较低的维护负担:
void GetData(int& data)
{
Call_Int_Method(data);
}
void GetData(double& data)
{
Call_Double_Method(data); // This call will not compile if data is int
}
或者,根据Call_(int|double)_Method
的内部结构,您可以重载它们:
template <typename T>
void GetData (T &data) {
CallMethod (data);
}
void CallMethod (int &) {}
void CallMethod (double &) {}
但是,如果您限制T
的类型,使用重载,可能会更好地维护:
void GetData (int &data) { CallMethod (data); }
void GetData (double &data) { CallMethod (data); }
你也可以用类型特征来限制模板中的可实例化类型,但这可能会降低可维护性。
但也许你的情况不同,你有一个适用于所有类型的模板,除了一些精选的:
// general template
template <typename T>
void GetData (T &data) {...}
// function, technically not an overload
void GetData (std::string &data) {....}
// this is an overload of the GetData(std::string&)-function:
void GetData (std::map<int,int> &) {....}
有很多选择,没有唯一的正确答案。可能给你一些可维护代码的经验法则是最好的:
不完整和不正确的Real Advices列表(_):
- 模板可以很好,如果使用得当
- 模板可以被恐怖地疯狂使用
- 选择纯重载
- 不时地重新考虑你的问题,并试着判断你当时选择的解决方案是否仍然是最有意义的。也就是说,保持灵活性,学习重构。
- 总是争取可读性。
一个好的可读性检查是让你的代码休息几天,然后再检查一遍,看看你是否仍然知道你做了什么。如果一切正常,请在更长的时间间隔内重复此测试。
- 非类型指针和引用模板参数,以及在编译时如何/为什么解析它们.c++
- 根据编译时参数在 C 中重复代码
- 使用 SFINAE 作为模板参数的编译时递归
- 使用带有 ref 参数的成员函数创建线程时出现编译错误
- 使用 std::enable_if 限制派生类的模板参数时出现编译错误
- 带有自动参数的函数使用 GCC 编译,但不使用 Visual C++ 编译
- 为什么我的递归可变参数模板无法编译?
- 知道模板参数在编译时是否为 const char*?
- 可变参数模板未在 MSVC 中编译?
- 将函数类型作为模板参数传递不会编译
- 线程构造函数周围的可变参数模板包装器无法编译
- 编译错误:临时对象构造函数中缺少参数
- 在编译时收集模板参数
- 有没有办法根据命令行参数定义数组大小?运行时与编译时实例化?
- C++模板-基于参数编译成员函数
- C++编译时检查是否可以用某种类型的参数调用重载函数
- Visual Studio代码:C 编译参数-O
- Clang 无法使用模板元编程编译参数包扩展
- 如何更改和设置Rcpp编译参数
- Jamfile的可移植编译参数