テンプレートで正しいタイプのデータを返す方法は?


9
#include <iostream>
using namespace std;

template <class X, class Y>
Y big(X a, Y b)
{
   if (a > b)
      return (a);
   else return (b);
}

int main()
{
   cout << big(32.8, 9);
}

ここではCPPでテンプレートを使用しているためbigdoubleinttypeの引数をバイパスして関数を呼び出すと、である返答が必要ですdouble。ここではタイプで、の32代わりに返されます32.8

どのようにして希望の出力を得るのですか big関数の適切な戻り値の型を記述する方法は?


1
関数は1つの固定型のみを返すことができます。実行時にどのタイプを返すかを選択することはできません。
Jesper Juhl、

1
どのようstd::maxに実装されているかを確認したい場合があります。関数の戻り値の型は、C ++でのコンパイル時にわかっている必要があります。したがって、この戻り値の型をパラメーターのランタイム値に依存させることはできません。これが、そのような関数の場合、両方のパラメーターが同じタイプ(つまり、タイプXであるがYではない)である必要がある理由です。
Boris Dalstein、

回答:


12

関数は、コンパイル時に認識される必要がある戻りタイプを1つだけ持つことができます。ただし、を使用してstd::common_type、両方のパラメーターを暗黙的に変換できる型を返すことができます。

それは

#include <type_traits>
template <class X, class Y>
typename std::common_type<X,Y>::type big(X a, Y b)
{
   if (a > b)
      return a;
   else return b;
}

そして、それが実際に返すことを確認するためにdouble渡されるintdouble、私たちは行うことができます。

int main() {
    auto x = big(4.2,42);
    std::cout << std::is_same<decltype(x),double>::value;
}

どのプリント

1

PS:std::common_typescencesの背後で三項演算子を使用できるため、この解決策は他の回答(auto+三項)と大きく異なりません。の真の力std::common_typeは、任意の数のパラメーターを受け入れることです。


10

戻りの型はコンパイル時に決定する必要があります。限定されている場合は、条件付き演算子末尾のリターンを使用できます。

template <typename X, typename Y>
auto big(X&& a, Y&& b) -> decltype(a > b ? a : b) // ---> like this
{
   return  a > b ? a : b;
}

ライブを見る


ただし、 以上へのアクセスauto権がある場合は、次のように条件演算子と一緒に使用するとコンパイラが正しい型を推測するため、戻り値で十分です。

template <typename X, typename Y>
auto big(X a, Y b)
{
   return  a > b ? a : b;
}

ライブを見る


少なくともC ++ 14以降では、末尾の戻り型は不要です。
1

@ウォルナット良い点。もう1つのオプションは転送参照ですか?
JeJo

1
@JeJoはい、私はそれも問題ないと思いますが、どちらの引数も変更しておらず、戻り値の型はどちらの場合でも左辺値参照になるため(おそらく非const
クルミ

コメントは適用されなくなったので削除しましたが、値にパラメーターを使用できないという警告を回答に追加することをお勧めします。
クルミ

誰かがあなたのコードを見ると、渡されたパラメータが誰かが受け取る戻り値の型を決定しているようですが、そうではありません!aがbよりも大きい場合でも、常にdoubleが返されます。
クラウス

4

戻り値の型をとしてマークし、2番目のパラメーターとしてYを渡すintことYで、それがであることを明確に示しましたint。ここで起こっている驚きはありません。

#include <iostream>

template <typename X, typename Y>
decltype(auto) big(const X& a, const Y& b)  // return type can just be auto as well 
{
    return a > b ? a : b;
}

int main()
{
    std::cout << big(32.8, 9) << '\n';
    std::cout << big(9, 32.8) << '\n';
    std::cout << big(32.8, 90) << '\n';
    std::cout << big(90, 32.8) << '\n';
}

これにより、4つの正しい値がすべて画面に出力されます。

https://godbolt.org/z/fyGsmo

注意すべき重要なことの1つは、これは相互に比較できる型に対してのみ機能することです。つまり、コンパイラーは暗黙的に比較のために1つの型を別の型に変換します。

重要:未定義の動作を回避するために、パラメーターは参照によって取得する必要があります。これは私が頑固に固執している戻り値の型に関係しています。decltype(auto)型への参照を返すことができます。関数にローカルなもの(引数の数)を返すと、未定義の動作が発生します。


@walnut誤って参照を返すことは、このサイトが実際に行うよりもはるかに困難です。しかし、未定義の動作について知っておくと役に立ちます。これは私がとにかく書くコードではないようです。質問に対する答えです。
1

1
ああ。私はあなたの以前のコメントを2つの異なる点として読み、影響と原因ではありません。適切な編集を行うことができます。
1

追加の免責事項を追加しました。
1

2

これは、おそらくあなたの正確な状況に対する正しい解決策ではありません。他の答えは、あなたが望むものに非常に近い可能性があります。

ただし、何らかの理由で実行時にまったく異なる型を本当に返す必要がある場合、正しい解決策は(以降)std::variant、一種の型安全な共用体であるを使用することです。

#include <variant>

template <typename X, typename Y>
std::variant<X, Y> max(X a, Y b) {
  if (a > b)
    return std::variant<X, Y>(std::in_place_index_t<0>, a);
  else
    return std::variant<X, Y>(std::in_place_index_t<1>, b);
}

その場合、責任は呼び出し側が返された値を処理することであり、おそらく使用std::visitするなどです。


-2

Yはintであり、32.8をキャストするため、intを返します。big 32,82を呼び出すと、floatですが、8はintであり、関数の戻り値の型はYで、これもintです。

実行時に大きな戻り値の型を知る必要があるため、これを実際に修正することはできません。そのため、aとbを次のように同じ型にします。

    #include <iostream>
    using namespace std;

    template <typename X>

    X big (X a, X b)
    {
    if (a>b)
    return a;

    else return b;
    }

    int main()
    {
    cout<< big (32.8, 9.0);
    }
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.