【问题标题】:Using template specialization in C++在 C++ 中使用模板特化
【发布时间】:2011-01-09 19:49:22
【问题描述】:

如何使用具有 2 种不同输入类型和一种输出类型的模板特化来编写函数:

template <class input1, class input2, class output>

并返回 2 个数字的总和(整数/双精度数)。但是,如果我得到 2 个整数,我想返回一个整数类型,但对于整数和双精度的任何其他组合,我总是返回双精度。

我试图在不直接使用“+”运算符的情况下这样做,而是使用下一个函数:

double add_double_double(double a, double b) {return (a+b);}
double add_int_double(int a, double b) {return ((double)(a)+b);}
int   add_int_int(int a, int b) {return (a+b);}

【问题讨论】:

  • 在我看来您是在描述内置运算符 + ?
  • @icecrime:假设问题是,“我如何编写一个模板函数,它的返回类型与表达式a+b 的类型相同,a 和 b 是它的输入”? C++0x 中的答案是使用decltype 和尾随返回类型,但 C++03 呢?

标签: c++ templates


【解决方案1】:

如果你可以使用 C++0x,你可以这样做:

template <typename T, typename U>
auto add(T lhs, U rhs) -> decltype(lhs+rhs)
{
    return lhs+rhs;
}

【讨论】:

  • 阅读新 C++ 规范(该规范称为 C++0x)带来的变化。本质上它是在说:add() 的类型是 (lhs+rhs) 的任何类型(“-> decltype” 所做的),并且 add() 返回该类型(“auto” 所做的)。假设编译器知道 T 和 U 是什么类型,并且假设为 T + a U 定义了“+”,那么编译器就知道 (lhs+rhs) 是什么类型 - 因为定义了“+”。所以代码告诉编译器使用该信息并产生正确的输出。
  • @user550413:C++0x 是下一个 C++ 版本的代号,它(希望)会在今年完成。大多数现代编译器已经部分支持它。发布的代码@Polybos 仅定义了一个函数,其中返回类型是“无论lhs+rhs 的类型是什么”。 decltype 计算括号内表达式的类型,auto 在此上下文中仅表示返回类型在行尾指定,在 -&gt; 之后而不是函数名之前。跨度>
  • C++0x 允许您定义一个函数F,它返回一个R 类型的值作为auto F(...) -&gt; R,而不是传统的R F(...)。而这种新语法的原因基本上是为了让decltype能够以这里所示的方式使用。
  • 而这个 auto 和 -> decltype 的东西可能看起来很像 DWIM,直到您考虑到可能要求模板函数处理大量模板类型......没有这个 C++0x方法您必须为每种情况编写模板函数的不同专业化(正如我在回答中所做的那样)。如果每个输入有 10 种可能的类型,输出有 10 种可能的类型怎么办!使用 C++0x 方法,您只需要确保为每对类型 T 和 U 定义 (lhs+rhs) 操作...但无论如何您都必须使用早期版本的 C++。
  • “您必须为每种情况编写不同的模板函数特化” - 或者可能使用 TMP 创建一个元函数,将一对类型(输入)映射到单一类型(输出)。然后你会做类似template &lt;typename T, U&gt; my_tmp_stuff&lt;T,U&gt;::type add(T t, U u); 的事情。那么问题是my_tmp_stuff有多少可以系统化,有多少在特殊情况下需要处理。
【解决方案2】:

不要使用模板特化。改用重载。专门化功能很复杂,很少需要:

template <typename T1, typename T2>
double sum(T1 a, T2 b) { return a + b; }

int sum(int a, int b) { return a + b; }

当且仅当两个参数都是int时才会调用第二个版本;否则,将调用第一个版本。

【讨论】:

  • long 是整数类型。如果我用两个long 参数调用它,我会得到提问者想要的返回类型吗? (在这种情况下,我不知道提问者想要什么返回类型)
  • @Steve:从这个问题看来,OP 只对int 感兴趣,而不是其他整数类型。
  • @Konrad:显然,即使我发送 2 个整数,它也会输入第一个总和。
  • @user:它不在我的编译器上(g++ 4.5)——我刚刚测试过。
  • 我认为这种方式可以避免问题。理想情况下,我认为 OP 想要一个与 any 类型一起使用的函数模板,并且只推断出正确的返回类型(就像在 C++0x 中使用 decltype 所做的那样)。所以重载并不是一个很好的解决方案,因为它只适用于有限的类型集(你必须编写许多重载,而不是一个通用版本)。当然,重载通常比模板专业化更可取,但我认为无论如何这里并不真正需要专业化。
【解决方案3】:

函数模板的返回类型可能取决于模板参数的类型。在您的情况下,您可以执行以下操作:

template <typename L, typename R>
struct sum_traits { typedef double return_type; };

template <>
struct sum_traits<int, int> { typedef int return_type; };

template <typename L, typename R>
typename sum_traits<L, R>::return_type
sum(L l, R r) { 
  typedef typename sum_traits<L, R>::return_type ret_t;
  return ret_t(l) + ret_t(r); 
}

我认为ret_t 的显式转换实际上并不需要在这种情况下,但它展示了更多的技术。

这只处理整数,但这个想法很容易推广到处理更复杂的情况。

【讨论】:

  • 请注意,当您获得更复杂的特征时,您可以进一步委托类型计算。
【解决方案4】:

这可能是你需要的:

#include <iostream>
using namespace std;

template <class input1, class input2, class output> output add(input1 n1, input2 n2) {
   return (output) n1 + (output) n2;
}

int main(int argc, char **argv) {

   cout << add<int, int, int>(1,1) << endl;
   cout << add<float, int, float>(1.1f,1) << endl;
   cout << add<int, float, float>(1,1.1f) << endl;
   cout << add<float, float, int>(1.1f,1.1f) << endl;

   return 0;
}

结果:

quad: > ./a.out 
2
2.1
2.1
2
Sun 09 Jan 2011 12:57:57 PM MST

quad: > 

【讨论】:

  • 糟糕,您在上面看到“float”,想象一下您实际上看到了“double”。
  • 几乎,但我正在尝试(被要求)在没有“+”运算符的情况下做到这一点。得到 3 个函数而不是“+”(见上文:已编辑)
  • 那我不明白你想要什么。 “sum”表示加法,是吗?至少在处理整数或浮点数据时,您将需要一个运算符来进行加法。
  • 是的,但不是直接的。想象一下,您无法访问“+”运算符,而只能访问上面的 3 个添加函数。
  • 我不知道,伙计,也许超载'-'来做加法?为什么你没有'+',是不是在其他地方超载了?
【解决方案5】:

您的问题的答案在于C++ templates 书的第 15 章。本章有一个accumulator example 可以解决您的问题。在本章的后面,它将讨论提升特征,其中将解决添加两种不同类型的问题,并解决类型提升问题。

【讨论】:

    猜你喜欢
    • 2022-07-05
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-05-29
    • 2012-11-06
    • 2011-06-27
    • 1970-01-01
    相关资源
    最近更新 更多