4

クラスがあるとします

template <typename T>
class A {
 public:
  template <typename V>
    void f(std::tr1::shared_ptr<const std::vector<V> > v1, 
           std::tr1::shared_ptr<const std::vector<float> > v2) {}
};

以下はコンパイルされません。

 A<string> a;
  std::tr1::shared_ptr<std::vector<float> > v1(new std::vector<float>());
  std::tr1::shared_ptr<std::vector<float> > v2(new std::vector<float>());
  a.f(v1, v2);

コンパイラ エラーは次のとおりです。

error: no matching function for call to 'A<std::basic_string<char, std::char_traits<char>, std::allocator<char> > >::f(std::tr1::shared_ptr<std::vector<float, std::allocator<float> > >&, std::tr1::shared_ptr<std::vector<float, std::allocator<float> > >&)'

コンパイラは、最初の引数をstd::tr1::shared_ptr<std::vector<float> > 作成 できませんでした。std::tr1::shared_ptr<const std::vector<float> >ただし、2 番目の引数 (非テンプレート引数) については可能です。

これに対する 1 つの解決策は、呼び出しを f() に変更して、このように呼び出すことf<float>(...)です。
別の解決策は、v1 を shared_ptr として宣言してconst vector<float>.

  1. ここでテンプレートのインスタンス化の動作が非常に異なるのはなぜですか?
  2. メソッドへの as 引数を持つことについての私の理解はshared_ptr<const T>、メソッドがshared_ptrが指しているものを変更できないということです。shared_ptrs を生のポインタに変更し、を への生のポインタとして変更するとv1、コードは正常にコンパイルされます。テンプレート演繹を破るのは sについて何ですか?v2vectorsshared_ptr
4

2 に答える 2

4

関連タイプ?

ご存じのとおり、T*const T*は関連するタイプです。1次から2次への標準変換(資格変換)があります。

まず、とは一般的に関連するタイプではないA<T>ことを理解する必要があります。A<const T>これらのタイプは、異なるサイズ、表現、および目的を持つことができます。一方は定義できますが、もう一方は定義できません。

特に、A<const T>は const 修飾A<T>された ではないため、この 2 つの間に修飾変換はなく、C++ はユーザー定義の修飾変換を宣言できるほど柔軟ではありません。(ユーザーは標準変換を宣言できません。ユーザー定義変換のみを宣言できます。ユーザー定義変換は標準変換ではありません。)

したがって、ユーザー定義型は基本型とは根本的に異なります。基本型のように関連付けることはできません。

shared_ptr<>

shared_ptr<>は、互換性のある型のファミリを形成するように設計されています。shared_ptr<T>は暗黙shared_ptr<U>T*に に変換可能U*です。特に、shared_ptr<T>は暗黙的に に変換可能shared_ptr<const T>です。にも暗黙的に変換可能shared_ptr<void>であるため、同じ理由です。

shared_ptr<const T>shared_ptr<T>は特別な関係がないため、 から への変換は からshared_ptr<T>shared_ptr<const T>と区別されませshared_ptr<T>shared_ptr<void>。これらは 2 つの異なる変換にすぎませんが、いずれのコンテキストでも「優先」と見なすことはできません。変換元 (ランク = 変換) よりも優先さT*れるconst T*変換 (ランク = 完全一致) とは異なります。T*void*

関数テンプレート

推定されたテンプレート関数の引数では、いくつかの標準的な変換が許可されています。

  • 資格変換
  • いくつかのポインター変換: 派生から基底へ
  • ...

しかし、これまで見てきたように、型の間にそのような変換は存在しませんshared_ptr<>

これは、コンパイラがテンプレート パラメーターのすべての可能な型を列挙して関数テンプレートを変換することを許可されていたとしても、

template <typename V>
void f (shared_ptr<const T> v1);

関数プロトタイプの無限のセットに:

for every type T,
such that shared_ptr<const T> can be instantiated:
f (shared_ptr<const T>)

完全に一致しない限り、関数を呼び出すことはできません。宣言が与えられた場合

struct Base {};
struct Derived : Base {};

shared_ptr<Derived> d;

fプロトタイプのセットの中には、次のものがあります。

f (shared_ptr<const Base>)
f (shared_ptr<const Derived>)

f (d)したがって、これらの候補の両方が異なるユーザー定義の変換を伴うため、 の呼び出しはあいまいになります。

于 2011-12-15T04:06:14.017 に答える
3

ここでテンプレートのインスタンス化の動作が非常に異なるのはなぜですか?

Aは、変換コンストラクターを介してshared_ptr<T>暗黙的にaに変換できます。shared_ptr<const T>残念ながら、このような変換は、テンプレート引数の推定中には使用できません。

引数の推定を成功させるには、引数のタイプがパラメーターのタイプと正確に一致している必要があります。ユーザー定義の変換を含むほとんどの変換は使用されません。

配列からポインターへの減衰や関数から関数ポインターへの変換など、ごくわずかな非常に基本的な変換のみが許可されます(配列型または関数型の関数パラメーターはないため、これらの変換によって混乱が生じることはありません)。さらに、最上位のconst-およびvolatile-修飾子は完全に無視されます。

于 2011-04-08T20:09:45.393 に答える