具有通用引用的重载解析

Overload resolution with universal references

本文关键字:重载 引用      更新时间:2023-10-16

我有一个函数,它可以通过通用引用接受任何类型,并希望为特定类型重载它(其中一些类型本身是模板化的,尽管我认为这在这里并不重要)。不幸的是,我似乎不能以正确的顺序解决重载。

我本以为foo的第二个声明会更受欢迎,因为它更具体(更少模板化),尽管看起来我对重载解析的理解有所欠缺。有趣的是,将第二个声明改为按值取X使其打印"good, good",而将X改为非const引用则使其打印"bad, good"。显然,完全删除第一个声明会使它返回"good, good",因为没有其他选择。

为什么会这样呢?最重要的是,如果下面的代码不起作用,如何用这个签名重载函数呢?

#include <iostream>
#include <string>
class X {};
template<typename T>
inline std::string foo(T && rhs) {
    return "bad";
}
inline std::string foo(const X & rhs) {
    return "good";
}
int main() {
    std::cout << foo(X()) << std::endl;
    X x;
    std::cout << foo(x) << std::endl;
    return 0;
}
编辑:

也许一个更迂回的解决方案是间接地做到这一点。去掉foo的第一种形式,使用SFINAE检查是否存在有效的过载,如果没有,则调用foo_fallback

要回答您的问题。评论Kerre的答案,您可以尝试使用SFINAE:

#include <type_traits>
#include <string>
template <class T>
struct HasFooImpl_ {
  template <typename C>
  static std::true_type test(decltype(fooImpl(std::declval<C>()))*);
  template <typename C> 
  static std::false_type test(...);
  typedef decltype(test<T>(0)) type;
};
template <typename T>
using HasFooImpl = typename HasFooImpl_<T>::type;
template <typename T>
typename std::enable_if<HasFooImpl<T>::value, std::string>::type 
foo(T&& t)
{
  return fooImpl(std::forward<T>(t));
}
template <typename T>
typename std::enable_if<!HasFooImpl<T>::value, std::string>::type
foo(T&& t)
{
    return "generic!";
}

你必须实现一个函数fooImpl对于任何类型,你不希望被一般处理。

实现有点棘手,我首先尝试了enable_if<is_same<string, decltype(fooImpl(declval<C>()))>::value,但是对于后退!is_same<>::value给了我编译器错误,因为它也试图实例化decltype。

这个实现有一个注意事项,您可能想使用,也可能不想使用:如果T 可转换为定义了fooImpl的其他类型,则转换将开始。

您可以在这里看到整个操作:http://ideone.com/3Tjtvj

更新:如果您不想允许类型转换,它实际上变得更简单:

#include <type_traits>
#include <string>
template <typename T> void fooImpl(T);
template <typename T>
using HasFooImpl = typename std::is_same<std::string, decltype(fooImpl(std::declval<T>()))>;
template <typename T>
typename std::enable_if<HasFooImpl<T>::value, std::string>::type 
foo(T&& t)
{
  return fooImpl(std::forward<T>(t));
}
template <typename T>
typename std::enable_if<!HasFooImpl<T>::value, std::string>::type
foo(T&& t)
{
    return "generic!";
}

见http://ideone.com/miaoop

Xconst X的转换被认为比直接匹配T = XT = X &的模板化重载更糟糕。