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

时间:2016-03-16 10:44:09

标签: c++ templates

我正在尝试制作一些可以读取或写入不同类型的文件流。除了具有特定方法的阅读部分外,一切都有效。该方法在被调用时返回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,我可以完成这项工作但是这对我没有好处,因为我在这两个函数上使用了一些宏而我无法生成{{1}或者在调用宏之前对象自身。仅供参考,我使用的是Visual Studio 2015。

有没有办法让这项工作没有任何重大修改?我还发现了一个建议,基本上说你必须在一个函数中添加一个指针参数,然后用unique_ptr<A> object作为参数调用它,但这在我的例子中并不重要。

感谢您的帮助。

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

3 个答案:

答案 0 :(得分:3)

您似乎想要部分专业化,并且由于无法对功能进行部分专业化,您可以转发到课程:

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>{}();
}

答案 1 :(得分:1)

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

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 */
}

答案 2 :(得分:1)

  1. 您不能有两个函数的重载,只有返回类型不同。您必须使用SFINAE确保只为任何给定的模板参数启用了一个。
  2. 您尝试在第二次重载中推断模板参数的方式是错误的。目前,您必须在调用该函数时指定TDContainerType。我觉得你可能只希望传递一种类型,然后推断它是否是std::unique_ptr
  3. 您无法调用std::make_unique并指定删除类型。您必须使用新创建的对象调用std::unique_ptr构造函数。
  4. 您无需显式移动返回的std::unique_ptr
  5. 这是做你想做的事的一种方法。

    #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>();
    }