手动设置重载函数的优先级
Manually prioritize overloaded functions
我对同一个函数有多个重载。对于某些参数集,几个重载同样适用,我自然会得到 "ambiguous overload" 错误。我想以某种方式为这些功能定义优先级,以便在出现歧义的情况下选择优先级最小的那个。
我尝试制作一系列模板助手 类、"tags"、P<0>
、P<1>
、...,以便 P<N>
继承P<N+1>
有一些上限。因此,如果有两个函数 f(P<2>)
和 f(P<4>)
,我调用 f(P<0>)
,第一个被选中。
它在实践中不起作用。经典的"clean"例子
"int/long" 和 "long/int" 函数仍然会产生歧义。我试图在函数中添加几个标签参数来增加标签的 "weight",但它没有帮助。
能否以某种方式调整此方法?
constexpr static int MAX = 20;
template<int N, class Enable = void>
class P {};
template<int N>
class P<N, typename std::enable_if<N < MAX, void>::type> : public P<N+1> {};
void f(int, long, P<2>) {}
void f(long, int, P<5>) {}
int main() {
f(1, 2, P<0>());
}
首先,您可以简化标签类型构造,例如:
template <int N> struct P : P<N+1> { };
template <> struct P<MAX> { };
接下来,标签类型的自动扶梯仅在它们是您的超载的唯一决胜局时才起作用。也就是说,其他所有参数都是等价的——除了标签参数。您的调用仍然不明确的原因是您最终得到的转换序列是:
f(int, long, P<2>); // #1: Exact, Integral Conversion, P<0> -> ... -> P<2>
f(long, int, P<5>); // #2: Integral Conversion, Exact, P<0> -> ... -> P<2> -> ... -> P<5>
只有当 each 参数的转换序列至少与另一个重载参数的转换序列一样好时,一个重载才优于另一个重载。这不是真的:#1
在第一个参数中比 #2
更好,在第二个参数中更差。添加另一个参数不会改变该缺陷。
如果您在非不相交条件下使用 SFINAE,则标签类型有用:
template <class T, std::enable_if_t<cond_a<T>::value>* = nullptr>
void f(T&&, P<0> ) { ... }
template <class T, std::enable_if_t<cond_b<T>::value>* = nullptr>
void f(T&&, P<1> ) { ... }
template <class T, std::enable_if_t<cond_c<T>::value>* = nullptr>
void f(T&&, P<2> ) { ... }
f(whatever, P<0>{});
cond_a
、cond_b
和 cond_c
不需要不相交,并且每种情况下的第一个参数都相同。因此,对于 SFINAE 未删除的那些重载,最后的决胜局是标签。
我对同一个函数有多个重载。对于某些参数集,几个重载同样适用,我自然会得到 "ambiguous overload" 错误。我想以某种方式为这些功能定义优先级,以便在出现歧义的情况下选择优先级最小的那个。
我尝试制作一系列模板助手 类、"tags"、P<0>
、P<1>
、...,以便 P<N>
继承P<N+1>
有一些上限。因此,如果有两个函数 f(P<2>)
和 f(P<4>)
,我调用 f(P<0>)
,第一个被选中。
它在实践中不起作用。经典的"clean"例子 "int/long" 和 "long/int" 函数仍然会产生歧义。我试图在函数中添加几个标签参数来增加标签的 "weight",但它没有帮助。
能否以某种方式调整此方法?
constexpr static int MAX = 20;
template<int N, class Enable = void>
class P {};
template<int N>
class P<N, typename std::enable_if<N < MAX, void>::type> : public P<N+1> {};
void f(int, long, P<2>) {}
void f(long, int, P<5>) {}
int main() {
f(1, 2, P<0>());
}
首先,您可以简化标签类型构造,例如:
template <int N> struct P : P<N+1> { };
template <> struct P<MAX> { };
接下来,标签类型的自动扶梯仅在它们是您的超载的唯一决胜局时才起作用。也就是说,其他所有参数都是等价的——除了标签参数。您的调用仍然不明确的原因是您最终得到的转换序列是:
f(int, long, P<2>); // #1: Exact, Integral Conversion, P<0> -> ... -> P<2>
f(long, int, P<5>); // #2: Integral Conversion, Exact, P<0> -> ... -> P<2> -> ... -> P<5>
只有当 each 参数的转换序列至少与另一个重载参数的转换序列一样好时,一个重载才优于另一个重载。这不是真的:#1
在第一个参数中比 #2
更好,在第二个参数中更差。添加另一个参数不会改变该缺陷。
如果您在非不相交条件下使用 SFINAE,则标签类型有用:
template <class T, std::enable_if_t<cond_a<T>::value>* = nullptr>
void f(T&&, P<0> ) { ... }
template <class T, std::enable_if_t<cond_b<T>::value>* = nullptr>
void f(T&&, P<1> ) { ... }
template <class T, std::enable_if_t<cond_c<T>::value>* = nullptr>
void f(T&&, P<2> ) { ... }
f(whatever, P<0>{});
cond_a
、cond_b
和 cond_c
不需要不相交,并且每种情况下的第一个参数都相同。因此,对于 SFINAE 未删除的那些重载,最后的决胜局是标签。