11

CRTP基本クラスでテンプレート化されたメンバー関数の戻り値の型を推測することは可能ですか?

引数の型の推測はうまく機能しますが、戻り値の型では失敗します。以下の例を考えてみましょう。

#include <iostream>

template <typename Derived>
struct base
{
  template <typename R, typename T>
  R f(T x)
  {
    return static_cast<Derived&>(*this).f_impl(x);
  }
};

struct derived : base<derived>
{
  bool f_impl(int x)
  {
    std::cout << "f(" << x << ")" << std::endl;
    return true;
  }
};

int main()
{
  bool b = derived{}.f(42);
  return b ? 0 : 1;
}

これにより、次のエラーが発生します。

  bool b = derived{}.f(42);
           ~~~~~~~~~~^
crtp.cc:7:5: note: candidate template ignored: couldn't infer template argument 'R'
  R f(T x)
    ^
1 error generated.

私の直感的な仮定は、コンパイラがint引数 to の型を推測できる場合f、 return に対しても機能するはずboolです。テンプレートのインスタンス化時に両方の型が既知であるためです。

末尾の戻り型の関数構文を使用しようとしましたが、 に入れる有効な式が見つかりませんでしたdecltype

編集1

関数に 1 つ以上のテンプレート化された引数がある場合、Dietmar Kühl は、間接レイヤーを使用してテンプレートのインスタンス化を遅らせることに基づく解決策を提供しました。残念ながら、これは次のように基本クラス関数に引数がない場合は機能しません。

template <typename R>
R g()
{
  return static_cast<Derived&>(*this).g_impl();
}

依存型が存在しないため、同じ手法を使用しようとすると失敗します。このケースをどのように処理しますか?

編集2

Johannes Schaub が指摘したように、C++11 はデフォルトのテンプレート引数を備えているためg、任意の型に依存させてから、Dietmar のソリューションを適用することが常に可能です。

template <typename T = void>
auto g() -> typename g_impl_result<Derived, T>::type
{
  return static_cast<Derived&>(*this).g_impl();
}

編集3

この問題は C++14 にはもう存在しません。通常の関数の戻り値の型推定があり、単純に記述できるためです。

template <typename Derived>
struct base
{
  template <typename T>
  auto f(T x)
  {
    return static_cast<Derived&>(*this).f_impl(x);
  }

  auto g()
  {
    return static_cast<Derived&>(*this).g_impl();
  }
};

struct derived : base<derived>
{
  bool f_impl(int x)
  {
    return true;
  }

  double g_impl()
  {
    return 4.2;
  }
};
4

1 に答える 1