yohhoyの日記

技術的メモをしていきたい日記

多値返却×型推論 in C++ (1)

C++1z(C++17)で導入予定の Structured Bindings*1std::tupleコンストラクタの改善(→id:yohhoy:20150416)を組み合わせて、多値返却関数からの戻り値を型推論された個別変数にて受け取る方法。

// C++1z(C++17)
#include <cassert>
#include <string>
#include <tuple>
#include <type_traits>
using namespace std::string_literals;  // operator ""s

std::tuple<int, std::string, double> func()
{
  // List initializationによるtuple構築
  return { 42, "Hello"s, 3.14 };
}

int main()
{
  // Structured Bindingsによる個別型推論
  auto [i, s, d] = func();

  assert(i == 42 && s == "Hello"s && d == 3.14);
  static_assert(std::is_same_v<decltype(i), int>);
  static_assert(std::is_same_v<decltype(s), std::string>);
  static_assert(std::is_same_v<decltype(d), double>);
}

C++14

同等の処理をC++14標準規格範囲内で行う場合、下記コードのような対処が必要となる。

  • C++14時点では)std::tupleコンストラクタが無条件にexplicit指定されるため、std::make_tuple関数による明示的な構築処理が必要。*2
  • Structured Bindingsに相当する処理は、std::get<N>関数により明示的に記述する必要がある。
  • C++14時点では)std::is_same_v変数テンプレートが提供されないため、std::is_same<T,U>::value と記述する必要がある。*3
  • C++14時点では)static_assert構文への文字列リテラル指定が必須。例示コードでは空文字列""を指定。
#include <cassert>
#include <string>
#include <tuple>
#include <type_traits>
using namespace std::string_literals;  // operator ""s

std::tuple<int, std::string, double> func()
{
  // 実引数(42, "Hello"s, 3.14)を基にtuple型を構築
  return std::make_tuple(42, "Hello"s, 3.14);
}

int main()
{
  // 戻り値tuple型から各要素を分解
  auto rt = func();
  auto i = std::get<0>(rt);
  auto s = std::get<1>(rt);
  auto d = std::get<2>(rt);

  assert(i == 42 && s == "Hello"s && d == 3.14);
  static_assert(std::is_same<decltype(i), int>::value, "");
  static_assert(std::is_same<decltype(s), std::string>::value, "");
  static_assert(std::is_same<decltype(d), double>::value, "");
}

おまけ:Boost.Preprocessorを駆使して力技でエミュレーションする実装例Emulating C++17 Structured Bindings in C++14も存在する。マクロのご利用は計画的に。

関連URL

*1:Structured Bindings(構造化束縛)は同機能を指す名前だが、C++言語仕様上は decomposition declaration と呼ばれる構文を用いる。2017-06-16追記:P0615R0で structured binding declaration に名称変更された。

*2:C++14 13.3.1.7/p1:"When objects of non-aggregate class type T are list-initialized, (snip), if an explicit constructor is chosen, the initialization is ill-formed."

*3:C++標準ライブラリ提供の「値を返すメタ関数」は、C++11/14の範囲内で std::is_same<T,U>::{} とも記述できる。(→id:yohhoy:20121207