自動戻り型 (C++11/14/20)

前回の投稿で、「自動戻り型 (C++98)」についての議論を開始しました。今日、私は同じ課題に直面していますが、C++11、C++14、および C++20 で解決しています。

念のために言っておきますが、これが私が解決したい課題です。

template <typename T, typename T2>
??? sum(T t, T2 t2) {
 return t + t2;
}

関数テンプレート がある場合 少なくとも 2 つの型パラメーターを使用すると、一般に関数の戻り値の型を決定できません。もちろん、sum  算術演算の型を返す必要があります t + t2

std::cout << typeid(5.5 + 5.5).name(); // double
std::cout << typeid(5.5 + true).name(); // double
std::cout << typeid(true + 5.5).name(); // double
std::cout << typeid(true + false).name(); // int

全文を読みたい場合は、前回の投稿「自動戻り型 (C++98)」をお読みください。ここで、C++11 にジャンプします。

C++11

C++11 では、この問題を解決する基本的に 2 つの方法があります:型特性または auto decltype との組み合わせ .

型特性

Type-traits ライブラリには関数 std::common_type があります .この関数は、コンパイル時に、任意の数の型の共通型を決定します。共通の型は、すべての型を暗黙的に変換できるすべての型の型です。この型が利用できない場合、コンパイル時エラーが発生します。

// automaticReturnTypeTypeTraits.cpp

#include <iostream>
#include <typeinfo>
#include <type_traits>

template <typename T, typename T2>
typename std::common_type<T, T2>::type sum(T t, T2 t2) {
 return t + t2;
}


int main() {

 std::cout << '\n';

 std::cout << typeid(sum(5.5, 5.5)).name() << '\n'; // double
 std::cout << typeid(sum(5.5, true)).name() << '\n'; // double
 std::cout << typeid(sum(true, 5.5)).name() << '\n'; // double
 std::cout << typeid(sum(true, false)).name() << '\n'; // bool

 std::cout << '\n';

}

簡単にするために、ソース コードでは型の文字列表現を表示します。 MSVC コンパイラを使用しました。 GCC または Clang コンパイラは、double に対して d などの単一文字を返します。 bool の場合は b。

std::common_type には微妙な違いが 1 つあります。 前回の投稿とこの投稿で紹介した他のすべての亜種:std::common_type 一般的な型を返しますが、前回の投稿「自動戻り型 (C++98)」での私の特性ソリューションと auto に基づくソリューション この投稿では、式 t + t2 の型を返します に評価されます。

auto decltype と組み合わせて

auto の使用 C++11 で関数の戻り値の型を推測するのは冗長すぎます。

まず、いわゆる末尾の戻り値の型を使用する必要があり、次に decltype で戻り値の型を指定する必要があります。

// automaticReturnTypeTypeAutoDecltype.cpp

#include <iostream>
#include <typeinfo>
#include <type_traits>

template <typename T, typename T2>
auto sum(T t, T2 t2) -> decltype(t + t2) {
 return t + t2;
}


int main() {

 std::cout << '\n';

 std::cout << typeid(sum(5.5, 5.5)).name() << '\n'; // double
 std::cout << typeid(sum(5.5, true)).name() << '\n'; // double
 std::cout << typeid(sum(true, 5.5)).name() << '\n'; // double
 std::cout << typeid(sum(true, false)).name() << '\n'; // int

 std::cout << '\n';

}

auto sum(T t, T2 t2)  -> decltype(t + t2) を読む必要があります 次の方法で。 auto で表現します タイプがわからないことと、後でタイプを指定することを約束します。この指定は decltype で行われます 式: decltype(t + t2) .関数テンプレート sum の戻り値の型 算術式が評価される型です。この C++11 構文について私が気に入らない点は次のとおりです。同じ式を 2 回使用する必要があります t + t2 .これはエラーが発生しやすく、冗長です。末尾の戻り値の型の構文は一般に省略可能ですが、C++11 およびラムダでの戻り値の型の自動推定には必須です。

C++14 が自動戻り型の使用を簡素化するかどうか見てみましょう。

C++14

C++14 では、冗長性のない自動戻り値型推定の便利な構文が得られました。

// automaticReturnTypeTypeAuto.cpp

#include <iostream>
#include <typeinfo>
#include <type_traits>

template <typename T, typename T2>
auto sum(T t, T2 t2) {
 return t + t2;
}


int main() {

 std::cout << '\n';

 std::cout << typeid(sum(5.5, 5.5)).name() << '\n'; // double
 std::cout << typeid(sum(5.5, true)).name() << '\n'; // double
 std::cout << typeid(sum(true, 5.5)).name() << '\n'; // double
 std::cout << typeid(sum(true, false)).name() << '\n'; // int

 std::cout << '\n';

}

C++14 では、 auto を使用できます 戻り型として。

C++20 への最後のジャンプをしましょう。

C++20

C++20 では、制約のないプレースホルダーの代わりに制約付きのプレースホルダー (概念) を使用する必要があります。概念 Arithmetic の定義と使用 私の意図を明確に表現します。関数テンプレート sum では算術型のみが許可されます .

// automaticReturnTypeTypeConcepts.cpp

#include <iostream>
#include <typeinfo>
#include <type_traits>

template<typename T>
concept Arithmetic = std::is_arithmetic<T>::value;

Arithmetic auto sum(Arithmetic auto t, Arithmetic auto t2) {
 return t + t2;
}


int main() {

 std::cout << '\n';

 std::cout << typeid(sum(5.5, 5.5)).name() << '\n'; // double
 std::cout << typeid(sum(5.5, true)).name() << '\n'; // double
 std::cout << typeid(sum(true, 5.5)).name() << '\n'; // double
 std::cout << typeid(sum(true, false)).name() << '\n'; // int

 std::cout << '\n';

}

概念 Arithmetic を定義しています type-traits 関数 std::is_arithmetic を直接使用する .関数 std::is_arithmetic いわゆるコンパイル時の述語です。コンパイル時関数は、コンパイル時に boolean を返す関数です。 .

概念について詳しく知りたい場合は、概念に関する以前の投稿をお読みください。

次は?

テンプレートを使用したコンパイル時のテンプレート メタプログラミングまたはプログラミングは、評判の悪い非常に強力な C++ 手法です。 std::common_type などの型特性ライブラリの関数 または std::is_arithmetic C++ でのテンプレート メタプログラミングの例です。次回の投稿では、テンプレートのメタプログラミングについて詳しく説明します。

Meeting C++ のための C++20 トレーニング

来週の火曜日 (2021 年 2 月 11 日) に、C++20 のビッグ 4 (概念、範囲、モジュール、およびコルーチン) に関する 1 日トレーニングを行います。私のトレーニングを予約すると、C++20 の本のクーポンももらえます。

お会いできてうれしいです、