0

テンプレートのインスタンス化の順序がわかりません。「遅すぎる」定義の場合、コンパイラは関数を考慮しないようです。次の手順は、以下のコードの主なアイデアを示しています。

  1. convert<From, To>関数の作業オーバーロードを見つけることができる場合、フレームワークはフリー関数を提供する必要がありますgenerate

  2. この関数to<T>は のショートカットでconvert<From,To>あり、有効な場合にのみ機能しconvert<From,To>ます。

  3. ユーザーは、 のオーバーロードを提供し、generateおよび を使用できる必要がtoありconvertます。

対応するコード:

#include <string>
#include <utility>
#include <iostream>

// If I move the code down below at [*] to this location, everything works as
// expected.

// ------------- Framework Code -------------

// Anything that can be generated can also be converted to a string.
template <typename From>
auto convert(From const& from, std::string& to)
  -> decltype(
      generate(std::declval<std::back_insert_iterator<std::string>&>(), from)
      )
{
  to.clear();
  auto i = std::back_inserter(to);
  return generate(i, from);
}

// Similar to convert, except that it directly returns the requested type.
template <typename To, typename From>
auto to(From const& f) -> decltype(convert(f, std::declval<To&>()), To())
{
  To t;
  if (! convert(f, t))
    throw std::invalid_argument("invalid conversion");
  return t;
}

// ------------- User Code -------------

// [*] Support arithmetic types.
template <typename Iterator, typename T>
auto generate(Iterator& out, T i)
  -> typename std::enable_if<std::is_arithmetic<T>::value, bool>::type
{
  // Note: I merely use std::to_string for illustration purposes here.
  auto str = std::to_string(i);
  out = std::copy(str.begin(), str.end(), out);
  return true;
}

int main()
{
  uint16_t s = 16;
  std::cout << to<std::string>(s) << std::endl;
  return 0;
}

generate次のコードの問題は、関数がandの定義の前にある場合にのみ機能することです。この問題を回避するにはどうすればよいですか?convertto

ここで私のメンタルモデルが間違っているのかもしれませんが、コンパイラが を参照すると、テンプレートは にto<std::string>(uint16_t)戻り、必要に応じてインスタンス化を開始すると思いました。任意のガイダンスをいただければ幸いです。

4

1 に答える 1

1

ご想像のとおり、コンパイラはとgenerateの定義を見るまでにの存在を知りません。あなたが思っていたことに反して、何が何であるかを見るまで、との定義を「保留」することはありません。この問題を回避するには、次の構造を使用して何ができるかを宣言する必要があります。converttoconverttogenerate generate

template <typename Iterator, typename T>
auto generate(Iterator& out, T i)
  -> typename std::enable_if<std::is_arithmetic<T>::value, bool>::type;

これは、 の定義の直前に表示する必要がありますconvert。これにより、コンパイラgenerateは実際に存在し、コンパイルするまでに関数であることをconvert認識できますto。このようにして、コンパイラは構文をチェックし、それが の有効な呼び出しであることを保証できます。これは、generategenerate が実際に何をするかを知る前であっても、この時点で行う必要があるのは、戻り値だけでなく引数の型が正しいかどうかをチェックすることだけだからです。言語標準で定義されたルールに従って一致します。

これを行うことで、自然に特定の型シグネチャを強制します(generateコンパイラはコンパイル時に型をチェックする必要があることに注意してください)。あなたがそれをしたくない場合、そしておそらくそうしない場合、最良のアプローチは、さらに同様に、 callable であると予想される、つまり、関数のように使用できるテンプレート引数を期待することです電話:converttoconvertto

template <typename From, typename Generator>
auto convert(From const& from, std::string& to, Generator generate)
  -> decltype(
      generate(std::declval<std::back_insert_iterator<std::string>&>(), from)
      )
{
  to.clear();
  auto i = std::back_inserter(to);
  return generate(i, from);
}

これらの種類のオブジェクトは、一般に呼び出し可能なオブジェクトとして知られています。

このアプローチの欠点は、残念ながら c++ はまだ概念generateをサポートしていないため、呼び出し可能なオブジェクトが注意を払うべき要件を強制するために多くのことを行うことができないことです。それにもかかわらず、このアプローチは標準ライブラリがそのアルゴリズムにうまく使用しているものです。

このアプローチの利点は、非常に柔軟にできることです。型の要件に最小限の注意を払う可能な任意の呼び出し可能オブジェクトを使用できますこれには、フリー関数、関数オブジェクト、バインディングによるメンバー関数などが含まれます。言うまでもなく、ユーザーは、有効な c++ である場合に最初のアイデアが必要とするように、使用を強制されるのではなく、呼び出し可能なオブジェクトに必要な名前を完全に自由に選択できます。generate

定義convertした free 関数を使用してこの修正版を呼び出すには、次のようにします。generate

to<std::string>(s, generate<std::back_insert_iterator<std::string>, uint16_t>);

テンプレートの引数を明示的に指定する必要があるため、この方法generateではテンプレート関数であるという事実を十分に活用できません。幸いなことに、この不便さは、たとえば次のような関数オブジェクトを使用することで克服できます。

struct Generator
{
    template <typename Iterator, typename T>
    auto operator()(Iterator& out, T i)
      -> typename std::enable_if<std::is_arithmetic<T>::value, bool>::type
    {
      // Note: I merely use std::to_string for illustration purposes here.
      auto str = std::to_string(i);
      out = std::copy(str.begin(), str.end(), out);
      return true;
    }
};

以前の呼び出しは単純になります

to<std::string>(s, Generator());

そのタンプレートの性質を最大限に活用しています。

いずれにせよ、私の考えが正しければ、コードのこの部分はユーザーの責任であるため、当然のことながら、ユーザーは自分が好む方法を決定する完全な自律性を持っています。

于 2013-09-04T01:24:56.683 に答える