在C ++中,如何根据类中的参数返回不同的泛型类型?

时间:2017-06-11 23:00:31

标签: c++ templates

我有这段代码:

template<class T1, class T2>
class Pair
{
private:
    T1 first;
    T2 second;

public:
    void SetFirst(T1 first)
    {
        this.first = first;
    }
    void SetSecond(T2 second)
    {
        this.second = second;
    }
    T1 GetFirst()
    {
        return first;
    }
    T2 GetSecond()
    {
        return second;
    }
};

我如何实现两个单一方法SetValue()GetValue(),而不是我所拥有的四个方法,它们取决于应该使用的泛型类型的参数?例如,我认为GetValue()方法可以采用1或2的int参数,并根据数字返回T1T2类型的变量。但我事先并不知道返回类型,所以无论如何都要解决这个问题?

3 个答案:

答案 0 :(得分:1)

不确定你想要什么,而不是你所要求的,但......

我建议使用如下定义的包装器基类

template <typename T>
class wrap
 {
   private:
      T  elem;

   public:
      void set (T const & t)
       { elem = t; }

      T get () const
       { return elem; }
 };

现在你的班级可以定义为

template <typename T1, typename T2>
struct Pair : wrap<T1>, wrap<T2>
 {
   template <typename T>
   void set (T const & t)
    { wrap<T>::set(t); }

   template <typename T>
   T get () const
    { return wrap<T>::get(); }
 };

或者,如果您可以使用C ++ 11和可变参数模板,并且如果您定义类型特征getType以获取列表的第N种类型,

template <std::size_t I, typename, typename ... Ts>
struct getType
 { using type = typename getType<I-1U, Ts...>::type; };

template <typename T, typename ... Ts>
struct getType<0U, T, Ts...>
 { using type = T; };

您可以更灵活地定义Pair,如下所示

template <typename ... Ts>
struct Pair : wrap<Ts>...
 {
   template <typename T>
   void set (T const & t)
    { wrap<T>::set(t); }

   template <std::size_t N, typename T>
   void set (T const & t)
    { wrap<typename getType<N, Ts...>::type>::set(t); }

   template <typename T>
   T get () const
    { return wrap<T>::get(); }

   template <std::size_t N>
   typename getType<N, Ts...>::type get ()
    { return wrap<typename getType<N, Ts...>::type>::get(); }
 };

现在set()的参数可以选择正确的基类和正确的基本元素

   Pair<int, long>  p;

   p.set(0);  // set the int elem
   p.set(1L); // set the long elem

否则,通过索引,你可以写

   p.set<0U>(3); // set the 1st (int) elem
   p.set<1U>(4); // set the 2nd (long) elem

不幸的是,get()没有收到参数,所以必须解释类型(通过类型或通过索引)

   p.get<int>();  // get the int elem value
   p.get<long>(); // get the long elem value

   p.get<0U>(); // get the 1st (int) elem value
   p.get<1U>(); // get the 2nd (long) elem value 

显然,当T1等于T2

时,这不起作用

以下是(C ++ 11)完整工作示例

#include <iostream>

template <std::size_t I, typename, typename ... Ts>
struct getType
 { using type = typename getType<I-1U, Ts...>::type; };

template <typename T, typename ... Ts>
struct getType<0U, T, Ts...>
 { using type = T; };

template <typename T>
class wrap
 {
   private:
      T  elem;

   public:
      void set (T const & t)
       { elem = t; }

      T get () const
       { return elem; }
 };

template <typename ... Ts>
struct Pair : wrap<Ts>...
 {
   template <typename T>
   void set (T const & t)
    { wrap<T>::set(t); }

   template <std::size_t N, typename T>
   void set (T const & t)
    { wrap<typename getType<N, Ts...>::type>::set(t); }

   template <typename T>
   T get () const
    { return wrap<T>::get(); }

   template <std::size_t N>
   typename getType<N, Ts...>::type get ()
    { return wrap<typename getType<N, Ts...>::type>::get(); }
 };

int main()
 {
   //Pair<int, int>  p;  compilation error
   Pair<int, long, long long>  p;

   p.set(0);
   p.set(1L);
   p.set(2LL);

   std::cout << p.get<int>() << std::endl;       // print 0
   std::cout << p.get<long>() << std::endl;      // print 1
   std::cout << p.get<long long>() << std::endl; // print 2

   p.set<0U>(3);
   p.set<1U>(4);
   p.set<2U>(5);

   std::cout << p.get<0U>() << std::endl; // print 3
   std::cout << p.get<1U>() << std::endl; // print 4
   std::cout << p.get<2U>() << std::endl; // print 5
 }

答案 1 :(得分:0)

C ++是静态类型的,因此给出的参数必须是模板参数而不是函数参数。

虽然它看起来只是用户的一个功能,但实际上只有两个。

template <int i = 1> auto GetValue() -> std::enable_if_t<i == 1, T1> { return first; }
template <int i = 2> auto GetValue() -> std::enable_if_t<i == 2, T2> { return second; }
template <int i = 1> auto SetValue(T1 x) -> std::enable_if_t<i == 1> { first = x; }
template <int i = 2> auto SetValue(T2 x) -> std::enable_if_t<i == 2> { second = x; }

我在返回类型上使用SFINAE来删除函数,除非模板参数是正确的。

答案 2 :(得分:0)

对于这种特殊情况,您绝对应该更喜欢std::pairstd::tuple

你可以简单地重载SetValue()(提供T1T2可以区分,如果不是你有编译错误):

void SetValue(T1 x)
{ first=x; }

void SetValue(T2 x)
{ second=x; }

然后,编译器找到任何调用的最佳匹配,即

Pair<int,double> p;
p.SetValue(0);    // sets p.first
p.SetValue(0.0);  // sets p.second

使用GetValue(),无法从p.GetValue()之类的内容推断出您要检索的元素的信息,因此您必须以某种方式提供它。有几个选项,例如

template<typename T>
std::enable_if_t<std::is_same<T,T1>,T>
GetValue() const
{ return first; }

template<typename T>
std::enable_if_t<std::is_same<T,T2>,T>
GetValue() const
{ return second; }

一样使用
auto a = p.GetValue<int>();
auto b = p.GetValue<double>();

但是你的初始版本还不错。