问题描述
我希望第一个代码示例的最后两行打印相同的内容。
类型按照我的预期被扣除,重载分辨率也是我预计。
然而,如果我显式地键入qualify函数调用,那么我得到一个不同的结果,然后当类型被推导。
第二个代码示例重复练习用专业代替重载分辨率。
任何解释?
编辑:我添加了一行显示了Karthik对于 print< R,int>(r);
的提及,我也不明白。
代码示例1:(函数模板重载)
#include< iostream&
template< typename T>
void print(T i)
{
std :: cout<< 简单<< std :: endl;
}
template< template< typename>类FF,类型名TT>
void print(FF< TT> i)
{
std :: cout< 模板<< std :: endl;
}
template< typename T1,typename T2>
void print(T1a)
{
T2 b;
std :: cout<< 两个类型参数< std :: endl;
}
模板<>
void print< int>(int i)
{
std :: cout< int< std :: endl;
}
template< typename T>
struct R
{
T x;
};
int main()
{
R< int> r;
print< int>(1.1); // ok,printsint
print(1.1); // ok,printssimple
print< int>(1); // ok,printsint
print(1); // ok,printsint
print(r); // ok,printstemplate
print< int,int>(1); // ok,打印两个类型参数
print< R< int>,int>(r); // ok,打印两个类型参数
print< R< int> >(r); //(1)??为什么简单?
print< R,int>(r); //(2)??打印模板,为什么它会编译?
// gcc 4.6.2(-std = c ++ 0x)和4.8.1(-std = c ++ 11)
// clang ++ 3.3.1与gcc相同的行为
}
代码示例2 :(类模板专门化)。
#include< iostream>
template< typename T>
struct P
{
static void print(T i)
{
std :: cout< 简单<< std :: endl;
}
};
template< template< class TT>类FF,类型名TT>
struct P< FF< TT> >
{
static void print(FF< TT> i)
{
std :: cout< 模板<< std :: endl;
}
};
模板<>
struct P< int>
{
static void print(int i)
{
std :: cout< int< std :: endl;
}
};
template< typename T>
struct R
{
T x;
};
int main()
{
R< int> r;
P< double> :: print(1.1); // ok,printssimple
P< int> :: print(1); // ok,printsint
P< R< int> > :: print(r); // ok,打印模板
// P< R,int> :: print(r); //确定,不编译
}
template< typename T> void print(T i); //(1)
template< template< typename>类FF,类型名TT> void print(FF TT; i); //(2)
template< typename T1,typename T2> void print(T1 a); //(3)
template<> void print< int>(int i); //(4)
好,一些预备:我们有三个函数模板1,2和3),4是1的特殊化。
所有三个重载都有一个函数参数。此外,函数具有模板参数:
1具有可从函数参数推导出的单一类型模板参数。
2有一个模板模板参数和一个类型模板参数,它们都可以从函数参数中推导出来。
3有两个类型的模板参数,只有第一个可以推导(使扣除无用)。
现在让我们看看调用。当有明确的模板参数时,编译器将总是预过滤那些可以实例化的函数的重载。
一个显式类型模板参数。 1匹配。 2不匹配,因为第一个参数不是模板。 3匹配,将 T1
固定为 int
;但是, T2
无法推导,因此也会消失。选择参数 T
为 int
时选择1。这符合专业化4。
print(1.1); // ok,printssimple
没有显式的模板参数。扣除开始;参数类型为 double
。 1匹配; T
是double。 2需要模式 FF
,并且 double
与不匹配,因此失败。 3可以推导 T1
到 double
,但没有 T2
,因此也失败。 1。专业化不匹配。
print< int>(1) //确定,打印int
这与第一种情况相同,分辨率,会发生隐式转换。
print(1); // ok,打印int
这与第二种情况相同,除了推导类型 int
(仍不匹配 FF
),因此专业化匹配。
print(r); //确定,打印模板
扣除给出以下结果: $ c> T = R< int> 。对于2, R< int>
匹配模式 FF
,因此是可行的, $ c> FF = R 和 TT = int
。 3,像往常一样,不知道如何处理 T2
。重载解析为1和2(同一性)得到相同的序列,所以部分功能模板排序解决了模糊性:2比1更专业,并被选择。
print< int,int>(1); // ok,打印两个类型参数
两个显式类型模板参数。 1只接受一个。 2想要一个模板作为第一个参数。剩下3个。
print< R< int>,int>(r) // ok,打印两个类型参数
第一个参数是 R< int>
而不是 int
,但是它仍然只是一个类型,而2不喜欢
print< R< int> >(r); //(1)??为什么简单?
这与第一种和第三种情况相同。我们有一个显式类型模板参数。 3无法推导出 T2
,2想要一个模板作为第一个参数,所以1是唯一的选择。 R 是一种类型,而不是模板。
; R,int>(r); //(2)?? printtemplate,
这里,我们有两个显式模板参数,第一个是模板,第二个一种。 1只接受一个参数。 3想要一个类型的第一个模板参数。
这里的关键教训是:
$ b b
- 在执行扣除或重载解决之前,显式模板参数与模板参数匹配,因此只有一些函数匹配。
- 1和2是过载,并且过载分辨率为他们单独发生。如果2是1的专业化,但是部分功能模板专业化不存在,那将是不同的。
- 只有一个模板,直到你给它的参数。实例化的函数模板是函数。实例化的类模板是类(因此是类型)。对于与参数扣除和部分专门化匹配的模式匹配,类模板和非模板类的实例化之间没有区别,除外。
编辑:回答扩展的问题。
这是一个透视的问题。没有类和函数的模式匹配规则,所以你不能说它们不同。有用于部分特化和模板参数扣除的模式匹配规则。这些都是一样的;关于部分特化的部分(14.5.5)指的是关于函数模板参数推导的部分(14.8.2)。
所以模式匹配规则是相同的。 / p>
但是,参数扣除只适用于函数(没有类模板的参数扣除,至少还没有),而部分专门化只适用于类专业功能)。这是函数和类之间的主要区别:在你的第一个例子中,你有两个函数模板:
template< typename T> ; void print(T i);
template< template< typename>类FF,类型名TT> void print(FF TT; i);
这是两个不同的模板。他们是完全独立的。它取决于显式参数传递,参数推导和重载解析的复杂规则和交互,以确定在任何给定调用中哪个意味着什么。然而,这是重要的,每个可以存在没有其他。换句话说,假设你只有一个函数:
模板< template< typename>类FF,类型名TT> void something_else(FF< TT> i);
然后你会惊讶于 something_else< R,int& ;
有效吗?你有一个有两个参数的模板,你传递两个参数。只有一个参数的另一个模板的存在不会改变这一点!
这很重要,所以我重复一遍:模板,即使它们具有相同的名称,也是完全独立的模板。
如果你尝试类同样的事情,编译器会抱怨:
template< typename T& class Q {};
template< template< typename>类FF,类型名TT> class Q {};
Clang说:
redef.cc:2:5:错误:模板重新声明中的模板参数过多
您不能有两个具有相同名称的类模板。编译器认为你想再次声明旧的 Q
,并且抱怨模板参数列表不匹配。
你可以用类模板做的唯一的事情是专门的,就像你在第二个例子:
template<类型名T> class P {};
template< template< typename>类FF,类型名TT> P< FF< TT> {};
但是请注意,这些不是独立的模板。他们甚至不是同一件事。第一个是类模板,第二个是类模板部分专门化。第二个完全依赖于第一个;删除主模板意味着专业化不再编译:
redef.cc:2:64:error:模板类'P'
与重载的函数模板不同,类模板部分特化不是一个实体用户可以参考。对于用户,有一个模板 P
,它有一个模板参数。如果此一个参数采用特定的形式,则专业化将匹配,但与第一个示例中的第二个功能模板不同,专业化不是具有两个参数的独立模板。
这就是为什么 P< R< int>> :: print(r)
编译和工作: P
参数,并为它传递 R 。部分特化与模式匹配,因此被选择。但 P 不起作用: P
只有一个模板参数,在这里你试图通过两个。专业化不是它自己的实体,因此不被考虑。
但是功能模板都是独立的,完整的模板。只有完整的专业模板<> void print< int>(int i)
不是。
总结:
$ b b
- 函数模板可以是完全专用的或重载的。重载的函数模板是完全独立的:当你想知道你可以或应该明确提供什么参数时,请依次查看它们的所有参数列表。
- 避免使用专门的函数模板。它与重载的函数模板以奇怪的方式交互,你可以结束一个不同于你期望被调用的函数。只需重载函数模板,与其他函数模板和纯函数。部分排序规则是直观的;只要记住,在有疑问时,就会在模板上选择一个普通函数。
- 类模板可以完全或部分专用,但不会重载。专业化不是独立的模板;当实例化类模板时,您总是必须转到参数列表的主模板。
- 用于选择部分专门化和从函数参数中导出模板参数的匹配规则是相同的;但是,当您将模板参数显式传递到函数模板时,不会对这些参数执行扣除。
I would expect the last two lines of the first code example to print the same.
The types are deducted as I expect and the the overload resolution is also as I expect.However, if I explicitly type qualify the function call, then I get a different result then when the type is deduced.
The second code example repeats the exercise replacing overload resolution with specialization. In that case everything works as anyone would expect.
Any explanation?
EDIT: I added one more line showing what Karthik was mentioning regarding print<R,int>(r);
which I also do not understand.
Code Example 1: (function template overloading)
#include <iostream>
template <typename T>
void print (T i)
{
std::cout << "simple" << std::endl;
}
template <template<typename> class FF, typename TT>
void print (FF<TT> i)
{
std::cout << "template" << std::endl;
}
template <typename T1, typename T2>
void print (T1 a)
{
T2 b;
std::cout << "two type parameters" << std::endl;
}
template <>
void print<int>(int i)
{
std::cout << "int" << std::endl;
}
template <typename T>
struct R
{
T x;
};
int main()
{
R<int> r;
print<int>(1.1); // ok, prints "int"
print(1.1); // ok, prints "simple"
print<int>(1); // ok, prints "int"
print(1); // ok, prints "int"
print(r); // ok, prints "template"
print<int,int>(1); // ok, prints "two type parameters"
print<R<int>,int>(r); // ok, prints "two type parameters"
print<R<int> >(r); // (1) ?? why "simple" ??
print<R,int >(r); // (2) ?? prints "template", why does it compile at all ??
// gcc 4.6.2 (-std=c++0x) and 4.8.1 (-std=c++11)
// clang++ 3.3.1 same behavior as gcc
}
Code Example 2: (class template specialization).
#include <iostream>
template <typename T>
struct P
{
static void print (T i)
{
std::cout << "simple" << std::endl;
}
};
template <template<class TT> class FF, typename TT>
struct P <FF<TT> >
{
static void print (FF<TT> i)
{
std::cout << "template" << std::endl;
}
};
template <>
struct P<int>
{
static void print(int i)
{
std::cout << "int" << std::endl;
}
};
template <typename T>
struct R
{
T x;
};
int main()
{
R<int> r;
P<double>::print(1.1); // ok, prints "simple"
P<int>::print(1); // ok, prints "int"
P<R<int> >::print(r); // ok, prints "template"
//P<R,int >::print(r); // ok, does not compile
}
解决方案 Well, let's look at what the compiler thinks of each of these.
template <typename T> void print (T i); // (1)
template <template<typename> class FF, typename TT> void print (FF<TT> i); // (2)
template <typename T1, typename T2> void print (T1 a); // (3)
template <> void print<int>(int i); // (4)
Ok, some preliminaries: we have three function templates here that overload each other (1, 2, and 3), and 4 is a specialization of 1.
All three overloads have a single function parameter. In addition, the functions have template parameters:
1 has a single type template parameter that can be deduced from the function parameter.
2 has a template template parameter and a type template parameter, both of which can be deduced from the function parameter.
3 has two type template parameters, only the first of which can be deduced (making the deduction useless).
Now let's look at the calls. When there are explicit template arguments, the compiler will always "pre-filter" the overloads for those functions that can be instantiated that way.
print<int>(1.1); // ok, prints "int"
One explicit type template argument. 1 matches. 2 doesn't match, because the first argument isn't a template. 3 matches, fixing T1
to int
; however, T2
cannot be deduced, so it falls away, too. 1 is chosen with the parameter T
being int
. This matches the specialization 4.
print(1.1); // ok, prints "simple"
No explicit template arguments. Deduction starts; the argument type is double
. 1 matches; T
is double. 2 requires the pattern FF<TT>
, and double
doesn't match that, so failure. 3 can deduce T1
to double
, but has nothing for T2
, so it fails too. 1 is chosen. The specialization doesn't match.
print<int>(1); // ok, prints "int"
This is identical to the first case, except that during final overload resolution, an implicit conversion happens.
print(1); // ok, prints "int"
This is identical to the second case, except that the deduced type is int
(still doesn't match FF<TT>
), so the specialization matches.
print(r); // ok, prints "template"
Deduction gives the following results: 1 matches, with T = R<int>
. For 2, R<int>
matches the pattern FF<TT>
, so it is viable, with FF = R
and TT = int
. 3, as usual, doesn't know what to do with T2
. Overload resolution gets identical sequences for 1 and 2 (identity), so partial function template ordering resolves the ambiguity: 2 is more specialized than 1 and is chosen.
print<int,int>(1); // ok, prints "two type parameters"
Two explicit type template arguments. 1 only accepts one. 2 wants a template as the first argument. 3 is left.
print<R<int>,int>(r); // ok, prints "two type parameters"
This is identical to the previous case. The first argument is R<int>
instead of int
, but that's still just a type and 2 doesn't like it.
print<R<int> >(r); // (1) ?? why "simple" ??
This is identical to the first and third cases. We have one explicit type template argument. 3 fails to deduce T2
, 2 wants a template as the first argument, so 1 is the only choice. R<int>
is a type, not a template.
print<R,int >(r); // (2) ?? prints "template",
Here, we have two explicit template arguments, the first a template, the second a type. 1 only accepts one argument. 3 wants a type for its first template parameter. 2 is happy to take the template for its first and the type for its second parameter.
The key lessons here are:
- Explicit template arguments are matched to the template parameters before any deduction or overload resolution happens, so only some functions match in the first place.
- 1 and 2 are overloads, and overload resolution happens separately for them. It would be different if 2 was a specialization of 1, but partial function template specialization doesn't exist.
- It's only a template until you give it arguments. An instantiated function template is a function. An instantiated class template is a class (and thus a type). There is no difference between the instantiation of a class template and a non-template class, except for the pattern matching that argument deduction and partial specialization do.
Edit: To answer the expanded question.
This is a matter of perspective. There are no pattern matching rules for classes and functions, so you can't say that they differ or not. There are pattern matching rules for partial specialization and for template argument deduction. These are actually the same; the section on partial specialization (14.5.5) refers to the section on function template argument deduction (14.8.2).
So the pattern matching rules are the same.
However, argument deduction only applies to functions (there's no argument deduction for class templates, at least not yet), while partial specialization only applies to classes (you can't partially specialize functions). This is the key difference between functions and classses: in your first example, you have two function templates:
template <typename T> void print(T i);
template <template <typename> class FF, typename TT> void print(FF<TT> i);
These are two different templates. They are completely independent. It's up to the complicated rules and interactions of explicit parameter passing, argument deduction, and overload resolution to determine which one is meant in any given invocation. However, and this is important, each can exist without the other. In other words, imagine you had only one function:
template <template <typename> class FF, typename TT> void something_else(FF<TT> i);
Would you then be surprised that something_else<R, int>(r);
is valid? You have a template with two parameters, and you pass two arguments. The existence of another template with just one argument doesn't change that!
This is important, so I'll repeat it: Two function templates, even if they have the same name, are completely independent templates.
Not so with classes. If you try the same thing with classes, the compiler will complain:
template <typename T> class Q {};
template <template <typename> class FF, typename TT> class Q {};
Clang says:
redef.cc:2:5: error: too many template parameters in template redeclaration
You cannot have two class templates with the same name. The compiler thinks you want to declare the old Q
again, and complains that the template parameter lists don't match.
The only thing you can do with class templates is specialize them, as you did in your second example:
template <typename T> class P {};
template <template <typename> class FF, typename TT> class P<FF<TT>> {};
But note that these are not independent templates. They are not even the same thing. The first is a class template, whereas the second is a class template partial specialization. The second is completely dependent on the first; removing the primary template means the specialization no longer compiles:
redef.cc:2:64: error: explicit specialization of non-template class 'P'
Unlike the overloaded function templates, a class template partial specialization is not an entity the user can reference. For the user, there is one template, P
, and it has one template parameter. The specialization will match if this one parameter takes a specific form, but unlike the second function template in the first example, the specialization is not an independent template with two parameters.
This is why P<R<int>>::print(r)
compiles and works: P
has one parameter, and R<int>
is passed for it. The partial specialization matches the pattern and is therefore chosen. But P<R, int>::print(r)
does not work: P
only has one template parameter, and here you're trying to pass two. The specialization is not its own entity and therefore is not considered.
But the function templates are all independent, full templates. Only the full specialization template <> void print<int>(int i)
is not.
So to sum up:
- Function templates can be fully specialized or overloaded. Overloaded function templates are fully independent: when you want to know what arguments you can or should explicitly supply, look at all their parameter lists in turn.
- Avoid specializing function templates. It interacts with overloaded function templates in weird ways, and you can end up with a function different from what you expected being called. Just overload function templates, with other function templates and plain functions. The partial ordering rules are intuitive; just remember that when in doubt, a plain function is chosen over a template.
- Class templates can be fully or partially specialized, but not overloaded. Specializations are not independent templates; when instantiating a class template, you always have to go to the primary template for the parameter list.
- The matching rules for choosing a partial specialization and for deducing template arguments from function arguments are the same; however, when you explicitly pass template arguments to a function template, deduction is not performed for these parameters.
这篇关于函数过载匹配模板模板的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!