C++模板函数具有相同的名称但不同的返回类型

C++ template functions with the same name but different return type

本文关键字:返回类型 函数 C++      更新时间:2023-10-16

我正在尝试制作一些可以读取或写入不同类型的文件流处理器。除了具有特定方法的阅读部分外,一切都有效。该方法在调用时返回std::unique_ptr<T>,并且是返回T的另一个方法的"包装器"。出于某种原因,编译器不使用此方法,而是尝试使用其他方法(返回 T 的方法)编译它。因此,编译失败。我已经尝试在互联网上搜索,但我找不到任何准确的答案。你能帮我解决这个问题吗?

我定义的两种方法:

template <typename T>
T read()
{
     T obj;
     obj.readFromFile<T>();
     return std::move(obj);
}

template <
  typename T,
  template<typename> class D,
  template<typename, typename> class Container
>
typename  std::enable_if_t<
  std::is_same<Container<T, D<T>>, std::unique_ptr<T, D<T>>>::value,
  Container<T, D<T>>
>
read()
{
    return std::move(std::make_unique<T, D<T>>(readFromFile<T>()));
}

后一种方法是我尝试调用的方法。

当我写这样的东西时:

std::unique_ptr<A> AfromFile = fileStreamer.read<std::unique_ptr<A>>()

编译器尝试使用第一种方法(template <typename T> T read() {...})编译它,编译失败。如果我先做unique_ptr object然后对*unique_ptr<A> object进行复制赋值,我可以完成这项工作,但这对我来说没有好处,因为我在这两个函数上使用了一些宏,并且我不能在调用宏之前unique_ptr<A> object或对象 A 自己做。仅供参考,我正在使用Visual Studio 2015。

有没有办法在没有任何重大修改的情况下完成这项工作?我还发现了一个建议,基本上是说你必须向一个函数添加一个指针参数,然后用static_cast<Obj>(nullptr)作为参数调用它,但这在我的示例中不算数。

感谢您的帮助。

更新:我只想说,下面的所有解决方案都对我有用,但是解决我问题的最简单方法是Barry提供的解决方案。再次感谢帮助我!

似乎你想要部分专业化,并且由于功能的部分专业化是不可能的,你可以转发到类:

template <typename T> struct helper
{
    T operator() const
    {
         T obj;
         obj.readFromFile<T>();
         return obj;
    }
};
template <typename T, typename D>
struct helper<std::unique_ptr<T, D>>
{
    std::unique_ptr<T, D> operator() const
    {
        return std::make_unique<T, D>(readFromFile<T>());
    }
};
template <typename T>
T read()
{
     return helper<T>{}();
}

问题是,虽然我理解你的意图:

std::unique_ptr<A> AfromFile = fileStreamer.read<std::unique_ptr<A>>();

实际上并没有调用你认为的函数。您有两个重载read

template <class T> T read();
template <class T,
    template<typename> class D,
    template<typename, typename> class Container
> T read();

第一个有一个模板参数,第二个有 3 个(还有一些 sfinae)。但是您只使用一个模板参数调用read(),因此第二个重载(您想要的重载)甚至不是一个选项。

对于这些情况,我喜欢简单地标记调度,以便我们可以重载,而不必专门化:

template <class T> struct tag{};
template <class T> T read() { return read(tag<T>{}); }
template <class T>
T read(tag<T> ) {
    T obj;
    obj.readFromFile<T>();
    return obj; // <== NB: no move() here! That inhibits RVO
}
template <class T, class D>
std::unique_ptr<T, D> read(tag<std::unique_ptr<T, D>> ) {
    /* unique_ptr case */
}
<小时 />
  1. 一个函数不能有两个重载,它们只因返回类型而异。您必须使用 SFINAE 来确保只为任何给定的模板参数启用一个。
  2. 您尝试在第二次重载中推断模板参数的方式是错误的。目前,调用函数时必须指定TDContainerType。我感觉您可能只想传递一种类型,然后推断它是否是std::unique_ptr
  3. 不能调用std::make_unique并指定删除程序类型。必须使用新创建的对象调用 std::unique_ptr 构造函数。
  4. 您无需显式移动返回的std::unique_ptr

这是做你想做的事的一种方法。

#include <memory>
#include <type_traits>
template<typename T>
T readFromFile() { return T(); }
template<typename T, typename D>
void helper(std::unique_ptr<T, D>);
template<typename T, typename = void>
struct is_unique_ptr : std::false_type {};
template<typename T>
struct is_unique_ptr<T, decltype(helper(std::declval<T>()))> : std::true_type {};
template<typename T, typename = std::enable_if_t<!is_unique_ptr<T>::value>>
T read()
{
     return readFromFile<T>();
}
template<typename P, typename = std::enable_if_t<is_unique_ptr<P>::value>, typename = void>
P read()
{
    using T = typename P::element_type;
    return P(new T(readFromFile<T>()));
}
int main()
{
  read<std::unique_ptr<int>>();
  read<int>();
}