在可变参数模板方法和普通模板方法之间进行选择的规则是什么?
What are the rules for choosing between a variadic template method and a usual template method?
我在 class 中有两个模板运算符:
template<class T>
size_t operator()(const T& t) const {
static_assert(boost::is_pod<T>(), "Not a POD type");
return sizeof t;
}
template<typename... T>
size_t operator()(const boost::variant<T...>& t) const
{
return boost::apply_visitor(boost::bind(*this, _1), t);
}
我将 boost::variant<some, pod, types, here>
作为参数传递给这些运算符。 GCC 4.8 和 llvm 6.0 可以很好地编译代码,选择 boost::variant
参数化运算符。 gcc 4.7 选择 const T& t
参数化运算符,因此由于静态断言而无法编译。
那么,我有一个疑问,这两者之间的选择规则是什么?
我觉得gcc 4.7肯定有bug,不过我没有证据
一般来说,编译器只是将所有推导的模板实例化视为潜在的重载,选择 "best viable function"(§ 13.3.3)。
确实这意味着 GCC 4.7 有一个错误。
请参阅第 14.8.3 节:过载解决方案
描述所有模板实例都将作为任何非模板声明的重载加入候选集:
A function template can be overloaded either by (non-template) functions of its
name or by (other) function templates of the same name. When a call to that
name is written (explicitly, or implicitly using the operator notation),
template argument deduction (14.8.2) and checking of any explicit template
arguments (14.3) are performed for each function template to find the template
argument values (if any) that can be used with that function template to
instantiate a function template specialization that can be invoked with the
call arguments. For each function template, if the argument deduction and
checking succeeds, the template- arguments (deduced and/or explicit) are used
to synthesize the declaration of a single function template specialization
which is added to the candidate functions set to be used in overload
resolution. If, for a given function template, argument deduction fails, no
such function is added to the set of candidate functions for that template. The
complete set of candidate functions includes all the synthesized declarations
and all of the non-template overloaded functions of the same name. The
synthesized declarations are treated like any other functions in the remainder
of overload resolution, except as explicitly noted in 13.3.3.
就您的问题而言,重载最终无法区分(来源:@Piotr S)。在这种情况下,应用 "partial ordering" (§14.5.6.2):
F1 and F2 are function template specializations, and the function template for F1 is more specialized than the template for F2
请注意,事情可能会变得非常棘手,例如"open template" 版本采用了 T&
而不是 T const&
(首选非 const 引用,其他条件相同)。
当您有多个重载最终具有相同的 "rank" 重载解析时,调用是 格式错误的 并且编译器将诊断出一个不明确的函数调用.
关键部分在[temp.deduct.partial]:
Two sets of types are used to determine the partial ordering. For each of the templates involved there is
the original function type and the transformed function type. [ Note: The creation of the transformed type
is described in 14.5.6.2. —end note ] The deduction process uses the transformed type as the argument
template and the original type of the other template as the parameter template. This process is done twice
for each type involved in the partial ordering comparison: once using the transformed template-1 as the
argument template and template-2 as the parameter template and again using the transformed template-2
as the argument template and template-1 as the parameter template.
即使对于 C++ 标准来说,这也确实很密集,但它的基本意思是这样的。以我们的两个重载为例:
template <class T> // #1
size_t operator()(const T& t) const
template <typename... T> // #2
size_t operator()(const boost::variant<T...>& t)
我们基本上会为每一个分配一些独特的类型,然后尝试看看另一个是否适用。因此,让我们为 #1
选择一些类型 A
,为 #2
选择 B,C,D
。 operator()(const A&)
对 #2
有用吗?否。operator()(const boost::variant<B,C,D>&)
是否适用于 #1
?是的。因此,部分排序规则表明 #2
比 #1
更专业。
因此,来自 [temp.func.order]:
The deduction process determines whether one of the templates is more specialized than the other. If
so, the more specialized template is the one chosen by the partial ordering process.
来自[over.match.best]:
[A] viable function F1
is defined to be a better function than another viable function
F2
if
— [..]
— F1
and F2
are function template specializations, and the function template for F1
is more specialized
than the template for F2
according to the partial ordering rules described in 14.5.6.2.
因此,在任何适用的情况下都应选择 #2
。如果 GCC 选择 #1
,那是不符合规范的行为,是一个错误。
我在 class 中有两个模板运算符:
template<class T>
size_t operator()(const T& t) const {
static_assert(boost::is_pod<T>(), "Not a POD type");
return sizeof t;
}
template<typename... T>
size_t operator()(const boost::variant<T...>& t) const
{
return boost::apply_visitor(boost::bind(*this, _1), t);
}
我将 boost::variant<some, pod, types, here>
作为参数传递给这些运算符。 GCC 4.8 和 llvm 6.0 可以很好地编译代码,选择 boost::variant
参数化运算符。 gcc 4.7 选择 const T& t
参数化运算符,因此由于静态断言而无法编译。
那么,我有一个疑问,这两者之间的选择规则是什么? 我觉得gcc 4.7肯定有bug,不过我没有证据
一般来说,编译器只是将所有推导的模板实例化视为潜在的重载,选择 "best viable function"(§ 13.3.3)。
确实这意味着 GCC 4.7 有一个错误。
请参阅第 14.8.3 节:过载解决方案
描述所有模板实例都将作为任何非模板声明的重载加入候选集:
A function template can be overloaded either by (non-template) functions of its name or by (other) function templates of the same name. When a call to that name is written (explicitly, or implicitly using the operator notation), template argument deduction (14.8.2) and checking of any explicit template arguments (14.3) are performed for each function template to find the template argument values (if any) that can be used with that function template to instantiate a function template specialization that can be invoked with the call arguments. For each function template, if the argument deduction and checking succeeds, the template- arguments (deduced and/or explicit) are used to synthesize the declaration of a single function template specialization which is added to the candidate functions set to be used in overload resolution. If, for a given function template, argument deduction fails, no such function is added to the set of candidate functions for that template. The complete set of candidate functions includes all the synthesized declarations and all of the non-template overloaded functions of the same name. The synthesized declarations are treated like any other functions in the remainder of overload resolution, except as explicitly noted in 13.3.3.
就您的问题而言,重载最终无法区分(来源:@Piotr S)。在这种情况下,应用 "partial ordering" (§14.5.6.2):
F1 and F2 are function template specializations, and the function template for F1 is more specialized than the template for F2
请注意,事情可能会变得非常棘手,例如"open template" 版本采用了 T&
而不是 T const&
(首选非 const 引用,其他条件相同)。
当您有多个重载最终具有相同的 "rank" 重载解析时,调用是 格式错误的 并且编译器将诊断出一个不明确的函数调用.
关键部分在[temp.deduct.partial]:
Two sets of types are used to determine the partial ordering. For each of the templates involved there is the original function type and the transformed function type. [ Note: The creation of the transformed type is described in 14.5.6.2. —end note ] The deduction process uses the transformed type as the argument template and the original type of the other template as the parameter template. This process is done twice for each type involved in the partial ordering comparison: once using the transformed template-1 as the argument template and template-2 as the parameter template and again using the transformed template-2 as the argument template and template-1 as the parameter template.
即使对于 C++ 标准来说,这也确实很密集,但它的基本意思是这样的。以我们的两个重载为例:
template <class T> // #1
size_t operator()(const T& t) const
template <typename... T> // #2
size_t operator()(const boost::variant<T...>& t)
我们基本上会为每一个分配一些独特的类型,然后尝试看看另一个是否适用。因此,让我们为 #1
选择一些类型 A
,为 #2
选择 B,C,D
。 operator()(const A&)
对 #2
有用吗?否。operator()(const boost::variant<B,C,D>&)
是否适用于 #1
?是的。因此,部分排序规则表明 #2
比 #1
更专业。
因此,来自 [temp.func.order]:
The deduction process determines whether one of the templates is more specialized than the other. If so, the more specialized template is the one chosen by the partial ordering process.
来自[over.match.best]:
[A] viable function
F1
is defined to be a better function than another viable functionF2
if
— [..]
—F1
andF2
are function template specializations, and the function template forF1
is more specialized than the template forF2
according to the partial ordering rules described in 14.5.6.2.
因此,在任何适用的情况下都应选择 #2
。如果 GCC 选择 #1
,那是不符合规范的行为,是一个错误。