C++ 函数模板偏特化?
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/8061456/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
C++ function template partial specialization?
提问by Narek
I know that the below code is a partial specialization of a class:
我知道下面的代码是一个类的部分特化:
template <typename T1, typename T2>
class MyClass {
…
};
// partial specialization: both template parameters have same type
template <typename T>
class MyClass<T,T> {
…
};
Also I know that C++ does not allow function template partial specialization (only full is allowed). But does my code mean that I have partially specialized my function template for one/same type arguments? Because it works for Microsoft Visual Studio 2010 Express! If no, then could you please explain the partial specialization concept?
我也知道 C++ 不允许函数模板部分特化(只允许完整)。但是我的代码是否意味着我已经部分地为一个/相同类型的参数专门化了我的函数模板?因为它适用于 Microsoft Visual Studio 2010 Express!如果不是,那么您能否解释一下部分专业化的概念?
#include <iostream>
using std::cin;
using std::cout;
using std::endl;
template <typename T1, typename T2>
inline T1 max (T1 const& a, T2 const& b)
{
return a < b ? b : a;
}
template <typename T>
inline T const& max (T const& a, T const& b)
{
return 10;
}
int main ()
{
cout << max(4,4.2) << endl;
cout << max(5,5) << endl;
int z;
cin>>z;
}
采纳答案by iammilind
In the example, you are actually overloading (not specializing)the max<T1,T2>function. Partial specialization syntaxshould have looked somewhatlike below (had it been allowed):
在示例中,您实际上是在重载(不是专门化)该max<T1,T2>函数。部分特化语法应该看起来有点像下面(如果它被允许):
//Partial specialization is not allowed by the spec, though!
template <typename T>
inline T const& max<T,T> (T const& a, T const& b)
{ ^^^^^ <--- specializing here
return 10;
}
[Note: in the case of a function template, only fullspecializationis allowed by the C++ standard (excluding the compiler extensions).]
[注:在一个功能模板的情况下,只有全专业化由C ++标准(不包括编译器扩展)允许的。]
回答by Rubens
Since partial specialization is not allowed -- as other answers pointed --, you could work around it using std::is_sameand std::enable_if, as below:
由于不允许部分专业化 - 正如其他答案所指出的那样 - 您可以使用std::is_sameand解决它std::enable_if,如下所示:
template <typename T, class F>
inline typename std::enable_if<std::is_same<T, int>::value, void>::type
typed_foo(const F& f) {
std::cout << ">>> messing with ints! " << f << std::endl;
}
template <typename T, class F>
inline typename std::enable_if<std::is_same<T, float>::value, void>::type
typed_foo(const F& f) {
std::cout << ">>> messing with floats! " << f << std::endl;
}
int main(int argc, char *argv[]) {
typed_foo<int>("works");
typed_foo<float>(2);
}
Output:
输出:
$ ./a.out
>>> messing with ints! works
>>> messing with floats! 2
Edit: In case you need to be able to treat all the other cases left, you could add a definition which states that already treated cases should not match-- otherwise you'd fall into ambiguous definitions. The definition could be:
编辑:如果您需要能够处理剩下的所有其他案例,您可以添加一个定义,指出已经处理过的案例不应匹配——否则您会陷入模棱两可的定义。定义可以是:
template <typename T, class F>
inline typename std::enable_if<(not std::is_same<T, int>::value)
and (not std::is_same<T, float>::value), void>::type
typed_foo(const F& f) {
std::cout << ">>> messing with unknown stuff! " << f << std::endl;
}
int main(int argc, char *argv[]) {
typed_foo<int>("works");
typed_foo<float>(2);
typed_foo<std::string>("either");
}
Which produces:
其中产生:
$ ./a.out
>>> messing with ints! works
>>> messing with floats! 2
>>> messing with unknown stuff! either
Although this all-casesthing looks a bit boring, since you have to tell the compiler everything you've already done, it's quite doable to treat up to 5 or a few more specializations.
虽然这种所有情况下的事情看起来有点无聊,因为你必须告诉编译器你已经完成的所有事情,所以处理多达 5 个或更多的专业化是完全可行的。
回答by Matthieu M.
What is specialization ?
什么是专业化?
If you really want to understand templates, you should take a look at functional languages. The world of templates in C++ is a purely functional sublanguage of its own.
如果你真的想了解模板,你应该看看函数式语言。C++ 中的模板世界本身就是一种纯函数式子语言。
In functional languages, selections are done using Pattern Matching:
在函数式语言中,选择是使用模式匹配完成的:
-- An instance of Maybe is either nothing (None) or something (Just a)
-- where a is any type
data Maybe a = None | Just a
-- declare function isJust, which takes a Maybe
-- and checks whether it's None or Just
isJust :: Maybe a -> Bool
-- definition: two cases (_ is a wildcard)
isJust None = False
isJust Just _ = True
As you can see, we overloadthe definition of isJust.
如您所见,我们重载了 的定义isJust。
Well, C++ class templates work exactly the same way. You provide a maindeclaration, that states the number and nature of the parameters. It can be just a declaration, or also acts as a definition (your choice), and then you can (if you so wish) provide specializations of the pattern and associate to them a different (otherwise it would be silly) version of the class.
好吧,C++ 类模板的工作方式完全相同。您提供一个主声明,说明参数的数量和性质。它可以只是一个声明,也可以作为一个定义(您的选择),然后您可以(如果您愿意)提供模式的特化并将它们关联到类的不同(否则会很愚蠢)版本.
For template functions, specialization is somewhat more awkward: it conflicts somewhat with overload resolution. As such, it has been decided that a specialization would relate to a non-specialized version, and specializations would not be considered during overload resolution. Therefore, the algorithm for selecting the right function becomes:
对于模板函数,特化有点尴尬:它与重载决议有些冲突。因此,已经决定特化将与非特化版本相关,并且在重载决议期间不会考虑特化。因此,选择正确函数的算法变为:
- Perform overload resolution, among regular functions and non-specialized templates
- If a non-specialized template is selected, check if a specialization exist for it that would be a better match
- 在常规函数和非专用模板之间执行重载决议
- 如果选择了非专业模板,请检查是否存在更匹配的专业化模板
(for on in-depth treatment, see GotW #49)
(有关深入处理,请参阅GotW #49)
As such, template specialization of functions is a second-zone citizen (literally). As far as I am concerned, we would be better off without them: I have yet to encounter a case where a template specialization use could not be solved with overloading instead.
因此,函数的模板特化是第二区公民(字面意思)。就我而言,没有它们我们会更好:我还没有遇到过模板特化使用无法通过重载来解决的情况。
Is this a template specialization ?
这是模板专业化吗?
No, it is simply an overload, and this is fine. In fact, overloads usually work as we expect them to, while specializations can be surprising (remember the GotW article I linked).
不,这只是一个过载,这很好。事实上,重载通常按我们的预期工作,而专业化可能令人惊讶(请记住我链接的 GotW 文章)。
回答by user2709407
Non-class, non-variable partial specialization is not allowed, but as said:
不允许非类、非变量的部分特化,但正如所说:
All problems in computer science can be solved by another level of indirection. —— David Wheeler
计算机科学中的所有问题都可以通过另一个间接层次来解决。——大卫·惠勒
Adding a class to forward the function call can solve this, here is an example:
添加一个类来转发函数调用可以解决这个问题,下面是一个例子:
template <class Tag, class R, class... Ts>
struct enable_fun_partial_spec;
struct fun_tag {};
template <class R, class... Ts>
constexpr R fun(Ts&&... ts) {
return enable_fun_partial_spec<fun_tag, R, Ts...>::call(
std::forward<Ts>(ts)...);
}
template <class R, class... Ts>
struct enable_fun_partial_spec<fun_tag, R, Ts...> {
constexpr static R call(Ts&&... ts) { return {0}; }
};
template <class R, class T>
struct enable_fun_partial_spec<fun_tag, R, T, T> {
constexpr static R call(T, T) { return {1}; }
};
template <class R>
struct enable_fun_partial_spec<fun_tag, R, int, int> {
constexpr static R call(int, int) { return {2}; }
};
template <class R>
struct enable_fun_partial_spec<fun_tag, R, int, char> {
constexpr static R call(int, char) { return {3}; }
};
template <class R, class T2>
struct enable_fun_partial_spec<fun_tag, R, char, T2> {
constexpr static R call(char, T2) { return {4}; }
};
static_assert(std::is_same_v<decltype(fun<int>(1, 1)), int>, "");
static_assert(fun<int>(1, 1) == 2, "");
static_assert(std::is_same_v<decltype(fun<char>(1, 1)), char>, "");
static_assert(fun<char>(1, 1) == 2, "");
static_assert(std::is_same_v<decltype(fun<long>(1L, 1L)), long>, "");
static_assert(fun<long>(1L, 1L) == 1, "");
static_assert(std::is_same_v<decltype(fun<double>(1L, 1L)), double>, "");
static_assert(fun<double>(1L, 1L) == 1, "");
static_assert(std::is_same_v<decltype(fun<int>(1u, 1)), int>, "");
static_assert(fun<int>(1u, 1) == 0, "");
static_assert(std::is_same_v<decltype(fun<char>(1, 'c')), char>, "");
static_assert(fun<char>(1, 'c') == 3, "");
static_assert(std::is_same_v<decltype(fun<unsigned>('c', 1)), unsigned>, "");
static_assert(fun<unsigned>('c', 1) == 4, "");
static_assert(std::is_same_v<decltype(fun<unsigned>(10.0, 1)), unsigned>, "");
static_assert(fun<unsigned>(10.0, 1) == 0, "");
static_assert(
std::is_same_v<decltype(fun<double>(1, 2, 3, 'a', "bbb")), double>, "");
static_assert(fun<double>(1, 2, 3, 'a', "bbb") == 0, "");
static_assert(std::is_same_v<decltype(fun<unsigned>()), unsigned>, "");
static_assert(fun<unsigned>() == 0, "");
回答by Puppy
No. For example, you can legally specialize std::swap, but you cannot legally define your own overload. That means that you cannot make std::swapwork for your own custom class template.
不可以。例如,您可以合法地专业化std::swap,但您不能合法地定义自己的过载。这意味着您无法std::swap为自己的自定义类模板工作。
Overloading and partial specialization can have the same effect in some cases, but far from all.
在某些情况下,重载和部分特化可以产生相同的效果,但远非全部。
回答by Aconcagua
Late answer, but some late readers might find it useful: Sometimes, a helper function – designed such that it can be specialised – can solve the issue, too.
迟到的答案,但一些迟到的读者可能会发现它很有用:有时,一个辅助函数——设计成可以专门化——也可以解决这个问题。
So let's imagine, this is what we triedto solve:
所以让我们想象一下,这就是我们试图解决的问题:
template <typename R, typename X, typename Y>
void function(X x, Y y)
{
R* r = new R(x);
f(r, y); // another template function?
}
// for some reason, we NEED the specialization:
template <typename R, typename Y>
void function<R, int, Y>(int x, Y y)
{
// unfortunately, Wrapper has no constructor accepting int:
Wrapper* w = new Wrapper();
w->setValue(x);
f(w, y);
}
OK, partial template function specialisation, we cannot do that... So let's "export" the part needed for specialisation into a helper function, specialize that one and use it:
好的,部分模板函数特化,我们不能这样做......所以让我们将特化所需的部分“导出”到一个辅助函数中,特化那个并使用它:
template <typename R, typename T>
R* create(T t)
{
return new R(t);
}
template <>
Wrapper* create<Wrapper, int>(int n) // fully specialized now -> legal...
{
Wrapper* w = new Wrapper();
w->setValue(n);
return w;
}
template <typename R, typename X, typename Y>
void function(X x, Y y)
{
R* r = create<R>(x);
f(r, y); // another template function?
}
This canbe interesting especially if the alternatives (normal overloads instead of specialisations, the workaroundproposed by Rubens, ... – not that these are bad or mine is better, just anotherone) would share quite a lot of common code.
这可能很有趣,特别是如果替代方案(正常重载而不是专业化,鲁本斯提出的解决方法,...... - 不是说这些不好或我的更好,只是另一个)将共享相当多的公共代码。

