1

API のマイナーな変更後、テンプレートの混乱の途中で突然現れたため、特定するのに何年もかかった奇妙なあいまいさに出くわしました。

次の例では、コンストラクターを呼び出すさまざまな方法を探っています (またはそう思っていました)。そのうちのいくつかは、私にはまったくわかりません。それらのすべてで、 type のオブジェクトを宣言しようとしていますA

#include <vector>
#include <cstdlib>
#include <iostream>
#include <typeinfo>
using namespace std;

// just a dummy class to use in the constructor of the next class
struct V {
   V(const std::vector<size_t> &){}
};

// the class we are interested in
struct A{
   A(const V &){}
   A(int, const V &){}
   A(const V &, int){}
   A(const V &, const V &){}
};

// a dummy function to delegate construction of V
V buildV(std::vector<size_t> &v){ return V(v); }

int main(){
   std::vector<size_t> v = {1024,1024};
   V vw(v);

   // I am using macros to make the constructor argument more visible
   #define BUILD_A(X) { A a(X); std::cerr << typeid(a).name() << std::endl; }
   #define BUILD_A2(X,Y) { A a(X, Y); std::cerr << typeid(a).name() << std::endl; }

   // All of the following call the constructor of A with different parameters
   // the comment on the right shows the type of the declared a
   /* 1 */ BUILD_A( vw )                       // object
   /* 2 */ BUILD_A( V(v) )                     // function pointer
   /* 3 */ BUILD_A( v )                        // object
   /* 4 */ BUILD_A( std::vector<size_t>() )    // function pointer
   /* 5 */ BUILD_A( (V)V(v) )                  // object
   /* 6 */ BUILD_A( ( V(v) ) )                 // object
   /* 7 */ BUILD_A( buildV(v) )                // object

   /* 8 */ BUILD_A2(10,V(v))                   // object
   /* 9 */ BUILD_A2(V(v),10)                   // object
   /* 10 */ BUILD_A2(vw,V(v))                  // object
   /* 11 */ BUILD_A2(V(v), vw)                 // object

   /* 12 */ //BUILD_A2(V(v), V(v))             // doesn't compile
   /* 13 */ BUILD_A2(V(v), (V)V(v))            // object
}

2 番目と 4 番目の例では、オブジェクトではなく関数ポインターを宣言しているように見えますが、これにはいくつか疑問があります。

  1. V(v)がオブジェクトではなく型として解釈されるのはなぜA a(V(v))ですか?
  2. 解釈されたものV(v)へのキャストバックはどのように異なりますか?(V)V(v)
  3. コンパイラがキャスト自体を推測できないのはなぜですか?
  4. 6の二重括弧((...))には意味的な意味がありますか、それともパーサーのあいまいさを解消するのに役立つだけですか? それがどのように優先順位の問題になるのかわかりません。
  5. V(v)オブジェクトではなく Type に評価される場合、なぜ 12 で合法ではないA a(V(v), V(v))のですか?
  6. スカラー値も追加すると、8 から 11 でコンパイラがもう一方もオブジェクトであることに突然気付くのは面白いことです。
  7. あいまいさを再現する構文を見逃していませんか? 他に紛らわしい事例を知っていますか。
  8. GCC は、そこに問題がある可能性があることを警告するべきではありませんか? Clangはそうします。

ありがとう、

4

1 に答える 1

1

これは、最も厄介な解析として知られています。試行された宣言は、関数宣言として解析されます。

C++ のルールは、何かが関数宣言として解析できる場合、それは解析されるというものです。

引数を持つA a(( V(v) ))関数の宣言として解析できず、を返すなど、いくつかの回避策があります。aVA


警告に関しては、標準ではこれに対する診断は必要ありません。結局、潜在的なあいまいさが解決されました。機能に賛成。:-)

于 2013-04-11T18:52:09.267 に答える