0

可能重复:
基于函数返回类型的模板推导?

我试图弄清楚 C++ 模板是如何工作的。例如,我想编写一个简单的数据结构,triple它的行为类似于 STL ,pair但包含三个项目。当然,我想使用模板来实现它。从我的角度来看,我正面临着一种奇怪的行为。

首先,我有一个工作示例。这是有效的 C++ 代码:

template<typename T1, typename T2>
T1 getSum(T1 a1, T2 a2) {
    return a1 + a2;
}

int main(int argc, int *argv[]) {
    long l1 = 1, l2 = 0, l3 = 0, l4 =0;
    short s1 = 2, s2 = 0, s3 = 0, s4 = 0;
    l2 = getSum(l1, s1);
    l3 = getSum(s1, l1);
    l4 = getSum(1, 2);
    s2 = getSum(l1, s1);
    s3 = getSum(s1, l1);
    s4 = getSum(1, 2);
    cout << l2 << " " << l3 << " " << l4 << endl;
    cout << s2 << " " << s3 << " " << s4 << endl;
    return 0;
}

这个程序的结果是两行包含3 3 3每一行。由于我getSum使用各种参数组合调用函数:(long, short)and(short, long)并且每次(integer literal, integer literal)都将返回值分配给longandshort变量,因此我假设编译器知道如何通过返回值进行适当的转换或猜测类型(在整数文字的情况下)每一次。

在这里,我们回到简单的triple实现。我以几乎相同的方式做所有事情(在我看来),但我得到一个转换错误。这就是我的意思。我的代码如下所示:

template <typename T1, typename T2, typename T3>
struct triple {
    T1 first;
    T2 second;
    T3 third;
};

template <typename T1, typename T2, typename T3>
triple<T1, T2, T3> mt(T1 f, T2 s, T3 t) {
    triple<T1, T2, T3> tr;
    tr.first = f;
    tr.second = s;
    tr.third = t;
    return tr;
};

int main(int argc, char* argv[]) {
    triple<char, char, unsigned short> t = mt(0, 0, 65000);
    cout << t.first << " " << t.second << " " << t.third << endl;
    return 0;
}

但是,当我尝试编译它时,出现以下错误:

22: error: conversion from 'triple<int, int, int>' to non-scalar type
'triple<char, char, short unsigned int>' requested

(第 22 行是main函数的第一行)。

毫不奇怪,上述两个triples 之间的转换不是编译器的工作。但令我惊讶的是,编译器无法确定由于我想得到函数triple<char, char, short unsigned int>的结果mt,因此模板参数应确定为T1 == char, T2 == char, T3 == unsigned short

而且由于现在事情是这样工作的,我对make_pair函数的工作方式感到困惑。它的代码看起来很像我的(再次,在我看来)。

所以,我的问题是:请澄清我的代码和工作代码之间的本质区别:)

4

4 回答 4

4

通常,C++ 不会根据该类型的使用方式来推断返回类型。相反,它根据您告诉它的返回类型推断返回类型。

template<typename T1, typename T2>
T1 getSum(T1 a1, T2 a2)

在这里,代码表示返回类型与第一个参数的类型相同T1。算术类型的问题是它们之间存在隐式转换,因此代码中的所有赋值语句都可以工作,要么是因为返回类型与被赋值的类型相同,要么是因为返回类型可以转换为分配给的类型。

template <typename T1, typename T2, typename T3>
triple<T1, T2, T3> mt(T1 f, T2 s, T3 t)

在这里,代码告诉编译器返回类型是 a triple,它的三个元素与函数的三个参数具有相同的类型。该调用mt(0, 0, 65000)具有三个 int 类型的参数,因此返回类型为triple<int, int, int>

于 2012-09-21T12:37:26.803 回答
3

您的问题是您的函数mt已经具有固定的返回类型:(Triple<int, int, int>由参数确定,int除非另有说明,否则始终认为整数常量)。一旦以这种方式创建,它就不能被分配,因为它是一个不同的类型,并且没有定义类型转换或构造函数来允许你尝试做的事情。

目前,您的结构中根本没有明确定义构造函数triple- 这意味着只有默认构造函数和复制构造函数(仅适用于完全相同的类型)!

但是有一个解决办法:看一下pair的定义:看拷贝构造函数(又是模板化,除了基类上的模板参数)。要使其适用于您的情况,请添加您自己的模板化复制构造函数:

template <typename T1, typename T2, typename T3>
struct triple {
    template <typename U1, typename U2, typename U3>    
    triple(U1 f, U2 s, U3 t):
        first(f),
        second(s),
        third(t) {}
    T1 first;
    T2 second;
    T3 third;
};

除了使用返回三元组的函数,您还可以简单地使用另一个带有三个参数的构造函数:

template <typename T1, typename T2, typename T3>
struct triple {
    triple(T1 f, T2 s, T3 t):
        first(f),
        second(s),
        third(t) {}
    T1 first;
    T2 second;
    T3 third;
};

然后主要你可以写:

triple<char, char, unsigned short> t(0, 0, 65000);

写的也少!

于 2012-09-21T12:29:13.983 回答
2

为了澄清编译器错误,它没有发生的原因std::pairmake_pairstd::pair有一个接受std::pair任何类型的模板构造函数。

Changing to the following code will mirror std::pair much more and resolve the compiler error:

template <typename T1, typename T2, typename T3>
struct triple {
    T1 first;
    T2 second;
    T3 third;

    triple() {}

    template <typename U1, typename U2, typename U3>
    triple(const triple<U1, U2, U3>& rhs)
      : first(rhs.first),
        second(rhs.second),
        third(rhs.third)
    {}
};
于 2012-09-21T12:44:36.120 回答
2

The reason it works for std::pair is not due to the functionality of std::make_pair, but instead is due to one of the extra constructors std::pair has.

Looking at the list of pair constructors, you will find that #4 is

template< class U1, class U2 >
pair( const pair<U1,U2>& p );

This allows std::pair to be initialized from any other pair, as long as U1 is convertible to T1, and U2 is convertible to T2.

To make it work with your triple, you would have to add the same sort of constructors/assignment operators. Note, std::tuple or boost::tuple already provide that functionality if they are available to you.

于 2012-09-21T12:44:36.523 回答