作为更广泛问题的一个例子,给定这两个重载,你可能会认为数组版本在传递数组时会优先:
template <size_t N>
void bar(const char (&)[N]) {
std::cout << "array, size=" << N-1 << std::endl;
}
void bar(const char *s) {
std::cout << "raw, size=" << strlen(s) << std::endl;
}
但是当传递数组(字符串文字是数组)bar("hello")
时,将调用后一版本(指针版本)。
关于SO的这个特殊情况has been discussed,答案很有趣。但这里有一个普遍的问题。我想强制编译器更喜欢一个重载,并且只在所有合法的调用尝试失败时才放弃该重载。
为了清楚起见,我们将其重命名为bar1
和bar2
:
template <size_t N>
void bar1(const char (&)[N]) {
std::cout << "array, size=" << N-1 << std::endl;
}
void bar2(const char *s) {
std::cout << "raw, size=" << strlen(s) << std::endl;
}
如果不进一步改变,我们可以写下这样的东西:
template<typename ...Args>
auto try_bar1_then_bar2(Args&& ...args) -> ??? {
... will first attempt to perfect forward to bar1 ...
... only if bar1 cannot be called, fallback to bar2 ...
}
我在这个问题中使用了一些C ++ 11,并且&amp;&amp;为了完美转发,但我想一般问题也适用于早期的C ++。是否有一种简单,通用的方法来强制重新排序过载优先级?当一组函数(具有不同的名称?)(几乎不可调用)时,如何准确控制它们的尝试顺序?
答案 0 :(得分:2)
一些表达SFINAE:
template<typename ...Args>
auto try_bar1_then_bar2_IMPL(int, Args&& ...args) -> decltype( bar1(forward<Args>(args)...) ) {
cout << "Yes, we can call bar1" << endl;
return bar1(forward<Args>(args)...);
}
template<typename ...Args>
auto try_bar1_then_bar2_IMPL(char, Args&& ...args) -> void {
cout << "No, can't call bar1, calling bar2 instead." << endl;
return bar2(forward<Args>(args)...);
}
template<typename ...Args>
auto try_bar1_then_bar2(Args&& ...args) -> decltype( try_bar1_then_bar2_IMPL(0, forward<Args>(args)...) ) {
return try_bar1_then_bar2_IMPL(0, forward<Args>(args)...);
}
如果无法调用bar1
,则try_bar1_then_bar2_IMPL
的第一次重载无效,因为返回类型中的decltype
失败。但是如果可以调用bar1
,那么两者都是有效的(并且我认为完全匹配)。因此,我在前面添加了一个虚拟参数,int
或char
,这打破了有利于bar1
的调用。
这就是这样称呼的:
try_bar1_then_bar2("hello"); // array, calls array version
try_bar1_then_bar2(+"hello"); // + converts to a pointer, therefore
// this calls the pointer ('raw') version.
答案 1 :(得分:0)
您可以使用模板包装参数以获取所需的重载函数:
#include <cstring>
#include <iostream>
template <typename T>
struct Wrap
{
const T& value;
Wrap(const T& value)
: value(value)
{}
};
template <typename T>
inline Wrap<T> wrap(const T& value) {
return Wrap<T>(value);
}
template <size_t N>
void bar(const Wrap<char[N]>&) {
std::cout << "array, size=" << N-1 << std::endl;
}
void bar(const Wrap<const char *>& s) {
std::cout << "raw, size=" << strlen(s.value) << std::endl;
}
template <typename T>
void bar(const T& value) {
bar(wrap(value));
}
int main(int argc, char* argv[]) {
const char a[] ="hello";
const char* s ="hello";
bar(a);
bar(s);
}