2016-03-16 118 views
3

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

两种方法我已经定义:

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,但是这对我来说并不合适,因为我在这两个函数上使用了一些宏,并且我无法在调用宏之前使对象A自己成为unique_ptr<A> object。仅供参考,我正在使用Visual Studio 2015.

有什么方法可以使这项工作没有任何重大修改?我还发现一个建议,基本上说你必须给一个函数添加一个指针参数,然后用static_cast<Obj>(nullptr)作为参数来调用它,但这不算在我的例子中。

感谢您的帮助。

更新: 我只想作出发言,下面所有的解决方案所做的工作对我来说不过来解决我的问题的解决方案通过提供巴里最简单的方法。 Thx再次帮助我!

+1

你将不得不调用'fileStreamer.read ,性病::的unique_ptr>()'... – Jarod42

+0

你说得对,确实是我调用这个方法用错了号码的参数。在我提出正确的参数数量后,代码编译 – Bected

回答

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

+0

谢谢你的建议。该解决方案是解决我的问题的最简单方法。哦,顺便说一句,我使用std :: move,因为我期望所有对象都应该有移动/分配构造函数。 – Bected

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. 你不能有一个函数的两个重载它只有返回类型不同。您必须使用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>(); 
} 
相关问题