0

重複の可能性:
戻り値の型に基づく関数のテンプレート推定?

C++ テンプレートがどのように機能するかを理解しようとしています。triple例として、 STL のように動作するが 3 つの項目を持つ単純なデータ構造を書きたいとし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それぞれを含む 2 行になります。getSum引数のさまざまな組み合わせで関数を呼び出すので(long, short)、戻り値は毎回変数と変数の両方に割り当てられるため、コンパイラーは戻り値によって適切な変換または型の推測を行う方法を知っていると仮定し(short, long)ます(integer literal, integer literal)(long整数リテラルの場合)。毎回。short

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関数の最初の行です)。

triple前述の 2 つの の間の変換がコンパイラの仕事ではないことは驚くことではありません。しかし、関数の結果として取得したいので、テンプレート引数を, ,として決定する必要があることをコンパイラが決定できないことは、私にとって驚きです。triple<char, char, short unsigned int>mtT1 == charT2 == charT3 == 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)

tripleここで、コードは、戻り値の型が であり、その 3 つの要素が関数の 3 つの引数と同じ型であることをコンパイラに伝えます。呼び出しmt(0, 0, 65000)には int 型の引数が 3 つあるため、戻り値の型はtriple<int, int, int>です。

于 2012-09-21T12:37:26.803 に答える
3

あなたの場合の問題は、関数mtの戻り値の型が既に固定されていることです(パラメーターによって決定され、特に指定されていない限りTriple<int, int, int>、整数定数は常にそうであると見なされます)。intそのように作成すると、それは別の型であり、あなたがしようとしていることを可能にする型変換またはコンストラクターが定義されていないため、割り当てることはできません。

現時点では、triplestrucht で明示的に定義されたコンストラクターはまったくありません。つまり、デフォルト コンストラクターとコピー コンストラクター (まったく同じ型に対してのみ機能します) しかありません。

しかし、解決策があります:ペアの定義を見てください: コピー コンストラクターを参照してください (基本クラスのテンプレート パラメーターに加えて、再びテンプレート化されます)。あなたのケースで機能させるには、独自のテンプレート化されたコピー コンストラクターを追加します。

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;
};

トリプルを返す関数を使用する代わりに、3 つのパラメーターを取る別のコンストラクターを使用することもできます。

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::pairですmake_pairstd::pairstd::pair

次のコードに変更すると、std::pairさらにミラーリングされ、コンパイラ エラーが解決されます。

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

それstd::pairが機能する理由は、の機能によるものではなくstd::make_pair、追加のコンストラクターの 1 つによるものstd::pairです。

ペア コンストラクターのリストを見ると、#4 が

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

これによりstd::pair、U1 が T1 に変換可能で、U2 が T2 に変換可能である限り、他のペアから初期化することができます。

トリプルで機能させるには、同じ種類のコンストラクター/代入演算子を追加する必要があります。注意してください、std::tupleまたはboost::tuple利用可能な場合はすでにその機能を提供しています。

于 2012-09-21T12:44:36.523 に答える