包含命名空间的类模板的转发声明会导致编译错误

时间:2017-12-28 03:09:20

标签: c++ templates compiler-errors namespaces forward-declaration

我有一个example1和一个example2,它使用前缀为相应类模板命名空间的类模板的前向声明。第一个例子用visual-studio编译好,而第二个例子没有。我检查了两个示例与其他编译器(http://rextester.com)。现在我有两个问题:

  • 在此处使用前向声明中的命名空间似乎是非法的。为什么呢?

  • Visual Studio(2015年和2017年)似乎允许在第一个示例中使用附加命名空间进行前向声明,但在第二个示例中则不允许。这是一个错误吗?

示例1:

<!DOCTYPE html>
<html>
  <head>
  </head>
  <body>
    <script>
      function initMap() {
        var uluru = {lat: 39.936489, lng: -98.128644};
        var map = new google.maps.Map(document.getElementById('map'), {
          zoom: 4,
          center: uluru
        });
        var marker = new google.maps.Marker({
          position: uluru,
          map: map
        });
      }

      function getDistances() {
        var waypts = [{location: 'Washington dc', stopover: true}, {location: 'virginia', stopover: true}];
        var bounds = new google.maps.LatLngBounds;
        //Services
        var distanceService = new google.maps.DistanceMatrixService;
        var distanceResponse = [];
        for (var counter = 1; counter < waypts.length; counter++) {
          distanceService.getDistanceMatrix({
              origins: [waypts[counter-1].location],
              destinations: [waypts[counter].location],
              travelMode: 'DRIVING',
              unitSystem: google.maps.UnitSystem.IMPERIAL,
              avoidHighways: false,
              avoidTolls: false
            },
            function(response, status) {
              if (status !== 'OK') {
                alert('Error was: ' + status);
              } else {
                var results = response.rows[0].elements;
                distanceResponse.push({duration: results[0].duration.value,    distance: results[0].distance.value});
              }
            }
          );
        }
        test(distanceResponse);
      }

      function test(distanceResponse) {
        console.log(distanceResponse); // Outputs an array of one element, which is the object
        console.log(distanceResponse[0]); // Outputs undefined
        console.log(distanceResponse[0].distance); // Outputs an error
      }

    </script>
    <div id="map" style="width: 622px; height: 274px; position:relative;overflow:auto; "></div>
    <button onclick="getDistances()">Get distances</button>
    <script defer src="https://maps.googleapis.com/maps/api/js?key=[MY-API-KEY]&callback=initMap">
    </script>
  </body>
</html>

c ++(gcc 5.4.0)
#include <vector> namespace N1 { template <typename T> struct MySystem {}; template <typename T> class Other {}; struct MyClass { MyClass() { typename Dependencies::TYPE_A oTYPE_A; } struct Dependencies { template <typename> class N1::Other; struct TypeX_Dependencies; using TYPE_A = N1::MySystem<N1::Other<TypeX_Dependencies>>; struct TypeX_Dependencies { using TYPE_A = typename Dependencies::TYPE_A; }; using TYPE_X = N1::Other<TypeX_Dependencies>; }; }; } int main(){ return 0; }

c ++(clang 3.8.0)
source_file.cpp:15:23: error: invalid use of template-name ‘N1::Other’ without an argument list class N1::Other;

c ++(vc ++ 19.00.23506 for x64 / also vs community 2017 15.4.4)
source_file.cpp:15:23: error: non-friend class member 'Other' cannot have a qualified name class N1::Other;
source_file.cpp:15:19: error: forward declaration of class cannot have a nested name specifier class N1::Other;

示例2:

compiles fine

c ++(gcc 5.4.0)
#include <vector> namespace N1 { template <typename T> struct MySystem {}; template <typename T> class Other {}; template <typename T> struct MyClass { MyClass() { typename Dependencies::TYPE_A oTYPE_A; } struct Dependencies { template <typename> class N1::Other; struct TypeX_Dependencies; using TYPE_A = N1::MySystem<N1::Other<TypeX_Dependencies>>; struct TypeX_Dependencies { using TYPE_A = typename Dependencies::TYPE_A; }; using TYPE_X = N1::Other<TypeX_Dependencies>; }; }; } int main(){ return 0; }

c ++(clang 3.8.0)
source_file.cpp:16:23: error: invalid use of template-name ‘N1::Other’ without an argument list class N1::Other;

c ++(vc ++ 19.00.23506 for x64 / also vs community 2017 15.4.4)
source_file.cpp:16:23: error: non-friend class member 'Other' cannot have a qualified name class N1::Other;
source_file.cpp:16:19: error: forward declaration of class cannot have a nested name specifier class N1::Other;

1 个答案:

答案 0 :(得分:1)

是的,这是一个错误。

根据[dcl.type.elab]/1

  

...如果elaborated-type-specifier是声明的唯一成分,则声明格式不正确,除非它是明确的专门化,明确的实例化或者它具有以下形式之一:

     
      
  • class-key attribute-specifier-seq opt identifier ;
  •   
  • friend class-key :: opt identifier ;
  •   
  • friend class-key :: opt simple-template-id ;
  •   
  • friend class-key nested-name-specifier identifier ;
  •   
  • friend class-key nested-name-specifier template opt simple-template- id ;
  •   

你的声明class N1::Other既不是一个明确的专业化,也不是一个明确的实例化,所以它必须有强调的形式(忽略那些friend声明)。请注意,强调形式中标识符之前不允许nested-name-specifier,因此声明格式错误。在没有模板的以下示例中,Clang的编译器错误显示了此问题。

namespace N {
    struct S;
}
struct N::S; // error: forward declaration of struct cannot have a nested name specifier

LIVE EXAMPLE(顺便说一下,GCC接受这个代码,只是发出一条警告,说明没有新的东西要宣布。我猜这是GCC的一个错误。)

此处template-head template <typename>无效,因为class N1::Other本身应根据 template-head <的语法定义形成声明 / em>,因此上面的段落适用。

粗略地说,声明与声明具有相同名称的类的范围不同的类的声明应该引入一个新类,在这种情况下, nested-name-specifier 不应该是使用或定义先前声明的类,在这种情况下,语法形成class-specifier而不是精心设计的类型说明符,因此上面的段落不适用。总之,这条规则是合理的。