次のテンプレート関数があるとします。
// #include <iostream>
template< typename T >
T read( std::istream& in )
{
T x;
in >> x; // (could check for failure, but not the point)
return x;
}
次のように使用することを意図しています (const
ユーザー入力から変数を初期化するため)。
// #include <string>
// std::istream& in = std::cin;
const int integer = read< int >( in );
const double decimal = read< double >( in );
const std::string word = read< std::string >( in );
...
ただし、同じ型を 2 回指定する必要があることに注意してください。1回は変数宣言用、もう 1 回は呼び出し用です。このような重複は避けたほうがよいでしょう ( DRY の原則、「Don't Repeat Yourself」も参照)。各変更は重複する必要があり、たとえば次のようなコードが必要になるためです。
const int integer = read< double >( in );
const double decimal = read< int >( in );
const std::string word = read< char* >( in );
「正常に」コンパイルされますが、おそらく実行時に悪いことをします(特に3番目のものは警告を引き起こします)。
型の繰り返しを避ける方法はありますか?
私はすでに 2 つの方法を考えているので、知らないふりをする代わりに、もっと質問する前にすぐにそれらを公開しています。
戻り型リゾルバー
最近、「Return Type Resolver」イディオムを発見しました(この質問から)。したがって、既存の関数の周りにテンプレートの暗黙的な変換演算子を使用して単純なクラスを作成できます。
class Read { std::istream& m_in; public: explicit Read( std::istream& in ) : m_in( in ) { } template< typename T > operator T() const { return read< T >( m_in ); } };
そして、次のようなコードを書くことができます:
const int integer = Read( in ); const double decimal = Read( in ); const std::string word = Read( in ); ...
ここで、「戻り値の型」は変数の型から自動的に推定されます (注:
const
変数がそうであっても戻り値の型はそうではないため、これは元のコードと真に同等です (特にインライン化の可能性がある後))。C++11
auto
C++11以降では、代わりに
auto
指定子を使用できます。これにより、型の名前を 2 回指定することも回避できますが、アプローチは「逆」です。const auto integer = read< int >( in ); const auto decimal = read< double >( in ); const auto word = read< std::string >( in ); ...
ここで、変数の型は戻り値の型から自動的に推測されます。
他に何か?
さて、あなたは他の選択肢を知っていますか?[この質問の「理由」については、以下のコメントへの私の返信を参照してください。]
ありがとうございました。