C ++模板问题添加两种数据类型

时间:2010-06-13 01:10:07

标签: c++ templates

我有一个带有重载+运算符的模板类。当我添加两个或两个双打时,这工作正常。如何将它添加到int和double并返回double?

template <class T>
class TemplateTest
{
private:
  T x;

public:

  TemplateTest<T> operator+(const TemplateTest<T>& t1)const
  {
    return TemplateTest<T>(x + t1.x);
  }
}

in my main function i have

void main()
{
  TemplateTest intTt1 = TemplateTest<int>(2);
  TemplateTest intTt2 = TemplateTest<int>(4);
  TemplateTest doubleTt1 = TemplateTest<double>(2.1d);
  TemplateTest doubleTt2 = TemplateTest<double>(2.5d);

  std::cout <<  intTt1 + intTt2 << /n;
  std::cout <<  doubleTt1 + doubleTt2 << /n;
}

我希望能够做到这一点

std::cout <<  doubleTt1 + intTt2 << /n;

9 个答案:

答案 0 :(得分:13)

这里有龙。你正在进入c ++的部分内容,这可能会导致很多问题发布到StackOverflow :)如果你真的想这样做,请仔细思考。

从简单部分开始,您希望允许operator+添加与T并不总是相同的类型。从这开始:

template <typename T2>
TemplateTest<T> operator+(const TemplateTest<T2>& rhs) {
  return TemplateTest<T>(this->x + rhs.x);
}

请注意,这是T2以及T的模板。添加doubleTt1 + intTt2时,T将为doubleTt1T2将为intTt2

但这是整个方法的大问题。

现在,当您添加doubleint时,您期望什么? 4 + 2.3 = 6.3?还是4 + 2.3 = 6?谁会期待6?您的用户应该,因为您将双重投回到int,从而丢失了小数部分。有时。取决于首先是哪个操作数。如果用户写了2.3 + 4,他们会得到(正如预期的那样?)6.3。困扰的图书馆使悲伤的用户。如何最好地处理?我不知道......

答案 1 :(得分:13)

斯蒂芬已经对你可能遇到的问题做了很好的解释。您可以为模板的所有实例化的所有可能组合定义重载(因此,您实际上已经为double + double,int + double,double + int等定义了运算符)。这可能很快变得难以处理,并且很难跟踪支持哪些组合。

使用名为Add()之类的非成员函数可能会更好。这样做的好处是您可以指定返回类型。缺点是您必须指定返回类型。 :-)一般而言,这比自动执行意外转换要好。

template <typename R, typename T, typename U>
TemplateTest<R> Add(const TemplateTest<T>& t, const TemplateTest<U>& u)
{
    return TemplateTest<R>(t.x + u.x);
}

调用为:

std::cout << Add<double>(intTt1, doubleTt1) << std::endl;

C ++ 0x将添加对许多语言功能的支持,这些功能将使这更简单,并允许您编写合理的operator+重载:

template <typename T, typename U>
auto operator+(const TemplateTest<T>& t, const TemplateTest<U>& u) 
    -> TemplateTest<decltype(t.x + u.x)>
{
    return TemplateTest<decltype(t.x + u.x)>(t.x + u.x);
}

这将确保执行通常的算术转换(整数提升,转换为浮点等)并最终得到预期的结果类型。

您的C ++实现可能已经支持这些C ++ 0x功能;你想查阅你正在使用的任何编译器的文档。

答案 2 :(得分:5)

  

我希望能够做到这一点

     

std :: cout&lt;&lt; doubleTt1 + intTt2&lt;&lt; “\ n” 个;

此案例可能需要的是类型特征。基本上,这些是包含typedef s的模板类。然后,部分专门化这样的模板以覆盖typedef s。

基本示例:

(这可能有点天真,但它应该得到基本的想法。)

template <typename A, typename B>
struct add_traits
{
    typedef A first_summand_t;   // <-- (kind of an "identity type")
    typedef B second_summand_t;  // <-- (ditto; both aren't strictly necessary)
    typedef B sum_t;             // <-- this is the interesting one!
};

现在你部分专门针对AB的各种组合使用

template<>
struct add_traits<int, int>
{
    typedef int first_summand_t;
    typedef int second_summand_t;
    typedef int sum_t;             // <-- overrides the general typedef
};

template<>
struct add_traits<int, double>
{
    typedef int first_summand_t;
    typedef double second_summand_t;
    typedef double sum_t;    // <-- overrides the general typedef
};

template<>
struct add_traits<double, int>
{
    typedef double first_summand_t;
    typedef int second_summand_t;
    typedef double sum_t;    // <-- overrides the general typedef
};

现在你可以编写一个相当通用的添加操作,如下所示:

template <typename A, typename B>
typename add_traits<A,B>::sum_t add(A first_summand, B second_summand)
{
    // ...
}

如您所见,您没有指定具体的返回类型;相反,您让编译器通过add_traits模板类来计算它。一旦编译器为特定的add函数生成代码,它将在相应的add_traits类中查找类型,并且由于您提供的部分专用版本,您可以确保应用某些类型的“组合”。


P.S。:同样的技术会例如当你想减去无符号数时也很有用。从另一个中减去一个unsigned int会导致否定答案;结果类型必须是(signedint


P.P.S。:根据以下评论进行更正。

答案 3 :(得分:3)

add运算符通常应该是一个自由函数,以避免更喜欢任何操作数类型,如@Stephen很好地解释。这样它就完全对称了。假设你有一个返回存储值的函数get,这可能如下所示(或者,如果这样的get函数不存在,你可以将它声明为朋友)

template<typename T1, typename T2>
TemplateTest<???> operator+(const TemplateTest<T1>& t1, const TemplateTest<T2>& t2)
{
  return TemplateTest<???>(t1.get() + t2.get());
}

现在的问题是找到一个结果类型。正如其他答案所示,这可以在C ++ 0x中使用decltype。您还可以使用?:运算符的规则来模拟这一点,这些规则大多非常直观。 promote<>是使用该技术的模板

template<typename T1, typename T2>
TemplateTest< typename promote<T1, T2>::type > 
operator+(const TemplateTest<T1>& t1, const TemplateTest<T2>& t2)
{
  return TemplateTest< typename promote<T1, T2>::type >(t1.get() + t2.get());
}

现在,例如,如果您添加doubleint,则会产生double作为结果。或者,如promote<>答案中所示,您还可以对promote进行专门化,以便将其直接应用于TemplateTest类型。

答案 4 :(得分:2)

如果这主要是针对基本类型,那么在新标准出现之前,您可以帮助自己使用元函数。

template<typename T1,
         typename T2,
         bool T1_is_int = std::numeric_limits<T1>::is_integer,
         bool T2_is_int = std::numeric_limits<T2>::is_integer,
         bool T1_is_wider_than_T2 = (sizeof(T1) > sizeof(T2)) > struct map_type;

template<typename T1, typename T2, bool b> struct map_type<T1, T2, b, b, true > { typedef T1 type; };
template<typename T1, typename T2, bool b> struct map_type<T1, T2, b, b, false> { typedef T2 type; };
template<typename T1, typename T2, bool b> struct map_type<T1, T2, false, true , b> { typedef T1 type; };
template<typename T1, typename T2, bool b> struct map_type<T1, T2, true , false, b> { typedef T2 type; };

template<typename T, typename U>
typename map_type<TemplateTestT<T>, TemplateTest<U> >::type
operator+(TemplateTest<T> const &t, TemplateTest<U> const &u) {
  return typename map_type<TemplateTest<T>, TemplateTest<U> >::type(x + t1.x);
}

当然,这最好与char_traits理念相结合:

template <typename A, typename B>
struct add_traits
{
  typedef A first_summand_t;
  typedef B second_summand_t;
  typedef typename map_type<A, B>::type sum_t;
};

这样你仍然可以专注于没有numeric_limits重载的类型。

哦,在生产代码中,你可能想要正确地命名它并在整数类型中添加有符号/无符号不匹配的东西。

答案 5 :(得分:2)

获取支持新C ++ 0x decltype运算符的编译器。

template < typename T1, typename T2 >
auto add(T1 t1, T2 t2) -> decltype(t1+t2)
{
  return t1 + t2;
}

现在你不必放弃那些“特质”类。

答案 6 :(得分:1)

您可以使用模板添加int和double值。在函数中,指定2个参数,并且在向函数传递值时,在尖括号中指定其类型。

示例:

//template
template<typename T1, typename T2>
void add(T1 a, T2 b)
{
    //for adding values
    cout<<"\n\nSum : "<<a+b;
}

int main ()
{
    //specify types while passing values to funcion
    add<int,double>(4,5.5454);
    add<float,int>(4.7,5);
    add<string,string>("hi","bye");
    return 0;
}

答案 7 :(得分:0)

通过为TemplateTest<double>

定义隐式案例,这在技术上是可行的
operator TemplateTest<double>() {
    return TemplateTest<double>((double)x);
}

实际上这可能不是一个好主意,因为x不一定能安全地转换为双倍;碰巧你在这里使用了TemplateTest<int>,但之后可能会使用TemplateTest<std::string>。你应该重新考虑你正在做的事情,并决定你是否确实需要这种行为

答案 8 :(得分:0)

旧问题的新答案。 对于C ++ 0x,您可以使用decltype,因为其他答案已经讨论过了。 我认为common_typedecltype更适合这种情况。

以下是通用添加中使用的common_type的示例:

#include <iostream>
#include <array>
#include <type_traits>

using namespace std;

template <typename T, typename U, unsigned long N>
 array<typename common_type<T, U>::type, N> // <- Gets the arithmetic promotion
 add_arrays(array<T, N> u, array<U, N> v)
{
    array<typename common_type<T, U>::type, N> result; // <- Used again here
    for (unsigned long i = 0; i != N; ++i)
    {
        result[i] = u[i] + v[i];
    }
    return result;
}

int main()
{
    auto result = add_arrays( array<int,    4> {1, 2, 3, 4},
                              array<double, 4> {1.0, 4.23, 8.99, 55.31} );

    for (size_t i = 0; i != 4; ++i)
    {
        cout << result[i] << endl;
    }

    return 0;
}

它基本上返回不同算术运算将提升的值。关于它的一个好处是它可以采用任意数量的模板args。注意:不要忘记在其末尾添加::type。这就是得到你想要的实际类型结果。

对于那些仍在使用pre-c ++ 11的人来说,还有一个common_type的提升版本