两个函数模板候选。在将一个参数作为参考后,选择不太专业的模板
Two function template candidates. After making one argument a reference, the less specialized template gets chosen
我有通用代码——Dijkstra 算法——我在不同的上下文中使用,所以我决定使用标签分派。
常用代码在以下函数中(可以看到End
根据Tag模板参数进行调度):
template <typename Tag, typename ... Args>
void Dijkstra(blahblah, Args&&... arg) {
...
if (End(Tag(), cost, n_id, distances, time_limit, args ...)) {
break;
}
对于大多数上下文,我定义了一个默认的无操作,如下所示:
template<typename ... Args>
bool inline End(Args&& ...) {
return false;
}
对于一个上下文,我使用以下签名定义函数:
bool inline End(OneContextTag, Duration d, NodeId n_id, Distances distances, Du time_limit, blahblah) {
一切都按预期进行,直到我发现我在Distances
之后的签名中忘记了&
– 我每次都在复制Distances,一个大unordered_map。
然而,在我将其更改为 const Distances&
以避免昂贵的复制之后,调用了不太专业的 noop 版本。我不知道为什么。以及如何修复它。
(我发誓只是添加了一个字符 &
。或者 const&
)
(签名在其他方面是正确的,如果我注释掉通用的 noop 版本,它只使用 OneContextTag
版本。)
(代码比较复杂,但希望能从这里算出来。)
我不知道为什么重载解析会像这里的 atm 那样工作。但我为您提供了一个潜在的解决方案,它(IMO)也更强大:
更改默认 End
以接受 UseDefaultEnd
标记作为第一个参数。对于每个应使用默认 End
的上下文,将其标签从 UseDefaultEnd
:
子类化
#include <iostream>
struct UseDefaultEnd {};
/* Comment first parameter; then you get the same behavior as
you're currently trying to solve. */
template<typename ... Args>
bool inline End(UseDefaultEnd, Args&& ...) {
// bool inline End(Args&& ...) {
return false;
}
struct OneTag {};
struct OtherTag : public UseDefaultEnd {};
bool inline End(OneTag, int const & i) {
return true;
}
template<typename Tag>
void Caller() {
int i = 42;
if (End(Tag(), i)) {
std::cout << "Used specific version of End" << std::endl;
}
}
int main() {
Caller<OtherTag>();
std::cout << "---" << std::endl;
Caller<OneTag>();
}
所以你问的基本上是为什么下面的程序打印 Special foo
但 Generic bar
:
struct A {};
template<class ... Args>
void foo(Args&&...)
{
std::cout << "Generic foo\n";
}
void foo(A)
{
std::cout << "Special foo\n";
}
template<class ... Args>
void bar(Args&&...)
{
std::cout << "Generic bar\n";
}
void bar(A const&)
{
std::cout << "Special bar\n";
}
int main()
{
A a;
foo(a);
bar(a);
}
让我们看看重载解析会发生什么:
1。候选函数被选中。
C++11/[over.match.funcs]/7
In each case where a candidate is a function template, candidate function template specializations are generated using template argument deduction (14.8.3, 14.8.2). Those candidates are then handled as candidate functions in the usual way.
呼叫foo(a)
的候选人:
template<> void foo<A&>(A&); // reference collapsing
void foo(A);
呼叫 bar(a)
的候选人:
template<> void bar<A&>(A&);
void bar(A const&);
2。 Select 最佳可行函数:
首先,如果(至少)其中一个参数具有更好的转换序列(并且没有其他参数具有更差的转换序列),则重载更好。
C++11/[over.ics.rank]/3
Standard conversion sequence S1 is a better conversion sequence than standard conversion sequence S2 if [ ... ] S1 and S2 are reference bindings (8.5.3), and the types to which the references refer are the same type except for top-level cv-qualifiers, and the type to which the reference initialized by S2 refers is more cv-qualified than the type to which the reference initialized by S1 refers.
这导致 bar
的候选模板的偏好,因为调用 void bar(A const&)
所需的转换需要将左值绑定到更多 cv 限定的 const 左值引用。
因此,您看到使用 Distances const&
时调用的通用版本。
C++11/[over.best.ics]/6
When the parameter type is not a reference [ ... ]
When the parameter has a class type and the argument expression has the same type, the implicit conversion sequence is an identity conversion.
这使得参数 a
传递给 void foo(A)
时的转换序列成为身份转换(模板函数也是如此)。
如果两个重载都没有更好的转换顺序,则非模板版本胜过模板。
C++11/[over.match.best]/1
[ ... ] Given these definitions, a viable function F1 is defined to be a better function than another viable function F2 if [ ... ] F1 is a non-template function and F2 is a function template specialization.
这就是 foo
的情况,并使您的代码在您使用 Distances distances
.
时按预期运行
我有通用代码——Dijkstra 算法——我在不同的上下文中使用,所以我决定使用标签分派。
常用代码在以下函数中(可以看到End
根据Tag模板参数进行调度):
template <typename Tag, typename ... Args>
void Dijkstra(blahblah, Args&&... arg) {
...
if (End(Tag(), cost, n_id, distances, time_limit, args ...)) {
break;
}
对于大多数上下文,我定义了一个默认的无操作,如下所示:
template<typename ... Args>
bool inline End(Args&& ...) {
return false;
}
对于一个上下文,我使用以下签名定义函数:
bool inline End(OneContextTag, Duration d, NodeId n_id, Distances distances, Du time_limit, blahblah) {
一切都按预期进行,直到我发现我在Distances
之后的签名中忘记了&
– 我每次都在复制Distances,一个大unordered_map。
然而,在我将其更改为 const Distances&
以避免昂贵的复制之后,调用了不太专业的 noop 版本。我不知道为什么。以及如何修复它。
(我发誓只是添加了一个字符 &
。或者 const&
)
(签名在其他方面是正确的,如果我注释掉通用的 noop 版本,它只使用 OneContextTag
版本。)
(代码比较复杂,但希望能从这里算出来。)
我不知道为什么重载解析会像这里的 atm 那样工作。但我为您提供了一个潜在的解决方案,它(IMO)也更强大:
更改默认 End
以接受 UseDefaultEnd
标记作为第一个参数。对于每个应使用默认 End
的上下文,将其标签从 UseDefaultEnd
:
#include <iostream>
struct UseDefaultEnd {};
/* Comment first parameter; then you get the same behavior as
you're currently trying to solve. */
template<typename ... Args>
bool inline End(UseDefaultEnd, Args&& ...) {
// bool inline End(Args&& ...) {
return false;
}
struct OneTag {};
struct OtherTag : public UseDefaultEnd {};
bool inline End(OneTag, int const & i) {
return true;
}
template<typename Tag>
void Caller() {
int i = 42;
if (End(Tag(), i)) {
std::cout << "Used specific version of End" << std::endl;
}
}
int main() {
Caller<OtherTag>();
std::cout << "---" << std::endl;
Caller<OneTag>();
}
所以你问的基本上是为什么下面的程序打印 Special foo
但 Generic bar
:
struct A {};
template<class ... Args>
void foo(Args&&...)
{
std::cout << "Generic foo\n";
}
void foo(A)
{
std::cout << "Special foo\n";
}
template<class ... Args>
void bar(Args&&...)
{
std::cout << "Generic bar\n";
}
void bar(A const&)
{
std::cout << "Special bar\n";
}
int main()
{
A a;
foo(a);
bar(a);
}
让我们看看重载解析会发生什么:
1。候选函数被选中。
C++11/[over.match.funcs]/7
In each case where a candidate is a function template, candidate function template specializations are generated using template argument deduction (14.8.3, 14.8.2). Those candidates are then handled as candidate functions in the usual way.
呼叫foo(a)
的候选人:
template<> void foo<A&>(A&); // reference collapsing
void foo(A);
呼叫 bar(a)
的候选人:
template<> void bar<A&>(A&);
void bar(A const&);
2。 Select 最佳可行函数:
首先,如果(至少)其中一个参数具有更好的转换序列(并且没有其他参数具有更差的转换序列),则重载更好。
C++11/[over.ics.rank]/3
Standard conversion sequence S1 is a better conversion sequence than standard conversion sequence S2 if [ ... ] S1 and S2 are reference bindings (8.5.3), and the types to which the references refer are the same type except for top-level cv-qualifiers, and the type to which the reference initialized by S2 refers is more cv-qualified than the type to which the reference initialized by S1 refers.
这导致 bar
的候选模板的偏好,因为调用 void bar(A const&)
所需的转换需要将左值绑定到更多 cv 限定的 const 左值引用。
因此,您看到使用 Distances const&
时调用的通用版本。
C++11/[over.best.ics]/6
When the parameter type is not a reference [ ... ]
When the parameter has a class type and the argument expression has the same type, the implicit conversion sequence is an identity conversion.
这使得参数 a
传递给 void foo(A)
时的转换序列成为身份转换(模板函数也是如此)。
如果两个重载都没有更好的转换顺序,则非模板版本胜过模板。
C++11/[over.match.best]/1
[ ... ] Given these definitions, a viable function F1 is defined to be a better function than another viable function F2 if [ ... ] F1 is a non-template function and F2 is a function template specialization.
这就是 foo
的情况,并使您的代码在您使用 Distances distances
.