Nir*_*man 12 c++ templates overload-resolution c++11
众所周知,函数模板不能部分专门用于C++.当您在概念上尝试实现此目标时,您可以使用两种可能的解决方案.其中一个是使用带有静态函数的结构,可选地用模板函数包装,如下所示:
template <class T, class U>
struct BarHelper
{
static void BarHelp(T t, const U& u)
{
std::cerr << "bar general\n";
}
};
template <class T>
struct BarHelper<T, double>
{
static void BarHelp(T t, const double& u)
{
std::cerr << "bar specialized\n";
}
};
template <class T, class U>
void bar(T t, const U& u)
{
BarHelper<T, U>::BarHelp(t, u);
};
Run Code Online (Sandbox Code Playgroud)
bar 这里是可选的,你可以直接使用结构的静态成员(尽管你必须明确指定所有参数).
另一种方法是重载函数模板:
template <class T, class U>
void func(T t, const U& u)
{
std::cerr << "func general\n";
}
template <class T>
void func(T t, const double& u)
{
std::cerr << "func specialized\n";
}
Run Code Online (Sandbox Code Playgroud)
对我来说,似乎第二种方法更可取.对于初学者来说,它更加冗长,而且对于意图更加清晰(我们正在编写函数,所以让我们使用函数而不是无意义的包装器结构).此外,您可以使用一些很好的技巧来控制重载分辨率.例如,您可以在继承层次结构中包含非模板化的"标记"参数,并使用隐式转换来控制函数的优先级.每当你在重载中具体指定一个类型时,你也会得到隐式转换,如果你不喜欢这种行为,你可以在你的重载上使用enable_if来阻止它(让你回到与结构相提并论).
是否有理由偏爱部分专业结构?这些原因有多普遍?即哪个应该是你的"默认"?如果您:a)计划自己实现所有特化,而b)这是否用作用户可以注入自己行为的自定义点,这会有所不同吗?
Herb Sutter有一篇关于避免功能模板专业化的着名博文.在其中,他还建议(接近结尾)偏爱部分专门的结构来重载功能模板,但他似乎没有给出任何具体的理由:http://www.gotw.ca/publications/mill17.htm.
道德#2:如果你正在编写一个函数库模板,宁愿把它写成一个永远不应该专门化或过载的单个函数模板
(重点补充).
让我们首先列出创建同一模板方法的多个变体的选项:
简单重载:这可以工作,正如问题提到和演示的那样。
然而,正如我们将在下面看到的,它并不总是能很好地工作。
使用函子类部分特化:这是不进行模板函数特化的直接替代方案。
std::enable_if与模板函数重载一起使用:当简单模板重载不起作用时可以选择此方法,请参见下文。
编辑:添加@Nir选项4
--- 编辑结束 ---
该问题提出了一种情况,当从调用中推导模板参数时,模板函数重载工作正常。然而,如果对模板函数的调用是直接提供模板参数,并且需要根据模板参数上的关系或条件来匹配实现,则重载就不再有帮助了。
template <typename T, T val1, T val2>
void isSame1() {
cout << "val1: " << val1 << ", val2: " << val2 << " are "
<< (val1==val2?" ":"NOT ") << "the same" << endl;
}
Run Code Online (Sandbox Code Playgroud)
尽管 val1 和 val2 在编译时已知,但无法部分特化我们在编译时知道它们相同的情况。函数重载在这种情况下没有帮助,对于两个非类型模板参数具有相同值的情况,没有重载。
通过类部分特化,我们可以做到:
template <typename T, T val1, T val2>
struct IsSameHelper {
static void isSame() {
cout << "val1: " << val1 << ", val2: " << val2 << " are NOT the same" << endl;
}
};
// partial specialization
template <typename T, T val>
struct IsSameHelper<T, val, val> {
static void isSame() {
cout << "val1: " << val << ", val2: " << val << " are the same" << endl;
}
};
template <typename T, T val1, T val2>
void isSame2() {
IsSameHelper<T, val1, val2>::isSame();
}
Run Code Online (Sandbox Code Playgroud)
或者,std::enable_if我们可以这样做:
template<typename T, T val1, T val2>
struct is_same_value : std::false_type {};
template<typename T, T val>
struct is_same_value<T, val, val> : std::true_type {};
template <typename T, T val1, T val2>
typename std::enable_if<!is_same_value<T, val1, val2>::value, void>::type isSame3() {
cout << "val1: " << val1 << ", val2: " << val2 << " are NOT the same" << endl;
}
template <typename T, T val1, T val2>
typename std::enable_if<is_same_value<T, val1, val2>::value, void>::type isSame3() {
cout << "val1: " << val1 << ", val2: " << val2 << " are the same" << endl;
}
Run Code Online (Sandbox Code Playgroud)
int global1 = 3;
int global2 = 3;
//======================================================
// M A I N
//======================================================
int main() {
isSame1<int, 3, 4>();
isSame1<int, 3, 3>();
isSame1<int*, &global1, &global1>();
isSame1<int*, &global1, &global2>();
isSame2<int, 3, 4>();
isSame2<int, 3, 3>();
isSame2<int*, &global1, &global1>();
isSame2<int*, &global1, &global2>();
isSame3<int, 3, 4>();
isSame3<int, 3, 3>();
isSame3<int*, &global1, &global1>();
isSame3<int*, &global1, &global2>();
}
Run Code Online (Sandbox Code Playgroud)
编辑:添加@Nir选项4
template <class T, T v> struct foo{
static constexpr T val = v;
};
// in a .cpp
template <class T, T v>
constexpr T foo<T, v>::val; // required for non-integral / non-enum types
template <class T, T v1, T v2> void isSame4(foo<T, v1> f1, foo<T, v2> f2) {
cout << "val1: " << f1.val << ", val2: " << f2.val << " are NOT the same" << endl;
}
template <class T, T v> void isSame4(foo<T, v> f1, foo<T, v> f2) {
cout << "val1: " << f1.val << ", val2: " << f2.val << " are the same" << endl;
}
Run Code Online (Sandbox Code Playgroud)
此选项的主要内容如下所示:
int global1 = 3;
int global2 = 3;
//======================================================
// M A I N
//======================================================
int main() {
isSame4(foo<int, 4>(), foo<int, 3>());
isSame4(foo<int, 3>(), foo<int, 3>());
isSame4(foo<int*, &global1>(), foo<int*, &global1>());
isSame4(foo<int*, &global1>(), foo<int*, &global2>());
}
Run Code Online (Sandbox Code Playgroud)
我没有看到选项 4 的语法有任何优势。但人们也可以有不同的想法……
请注意,选项 4 中需要 .cpp 文件,用于声明T foo::val,在所有其他选项中,所有内容都适合 .h 文件。
--- 编辑结束 ---
在我们可以获得编译时间分辨率的情况下,基于模板元编程,需要部分专业化。对于函数来说,这可以通过类部分特化或使用enable_if(它又需要其自己的类部分特化来满足其条件)来实现。
请参阅代码:http://coliru.stacked-crooked.com/a/65891b9a6d89e982
| 归档时间: |
|
| 查看次数: |
538 次 |
| 最近记录: |