我有以下代码:
#include <iostream>
template <typename T>
struct Base
{
using Type = int;
};
template <typename T>
struct Derived : Base<T>
{
//uncommmenting the below cause compiler error
//using Alias = Type;
};
int main()
{
Derived<void>::Type b = 1;
std::cout << b << std::endl;
return 0;
}
现在,Type
类型名称Derived
可用于推导上下文中的b
- 如Type
完全有效的声明所示。但是,如果我尝试在Derived
本身的声明中引用Type
,那么我会收到编译器错误,告诉我Alias
没有命名类型(例如,如果定义Type
未被注释。
我想这与编译器无法检查Derived
是否可以在基类中解析T
的定义时从基类中引入时有所作为。参数Base
的特定实例化。在这种情况下,这很令人沮丧,因为Type
总是定义T
而不管Derived
。所以我的问题有两个:
1)。为什么地球会发生这种情况?我的意思是为什么编译器在实例化上下文之外根本不解析using Alias = typename Base<T>::Type
(我猜是非推导的上下文),如果不这样做会避免这些'伪造'编译器错误?也许这是有充分理由的。标准中规定这必须发生的规则是什么?
2)。对于这类问题,有什么好的解决方法?我有一个真实的案例,我需要在派生类的定义中使用基类类型,但是由于这个问题我无法这样做。我想我正在寻找某种“隐藏在非推断上下文”解决方案之外的解决方案,我通过在模板化类或其他方面放置所需的定义/ typedef来防止这个编译器“首次通过”。
编辑:正如下面的一些答案所指出的那样,我可以使用Derived
。我应该从一开始就说,我知道这是有效的。但是,由于两个原因,它并不完全令人满意:1)它根本不使用继承层次结构(Base
不必从using
派生来实现这一点),而且我是正是尝试使用我的基类层次结构中定义的类型和2)真实案例实际上有几层继承。如果我想从几层中提取某些东西,这变得非常难看(我需要引用一个非直接的祖先,或者在每一层都重复 <html>
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.4.8/angular.min.js"></script>
<body>
<p>Click the table headers to change the sorting order:</p>
<div ng-app="myApp" ng-controller="namesCtrl">
<p>
<button type="button" ng-click="remove($index)">Delete Selected</button>
</p>
<table border="1" width="100%">
<tr>
<th>
<input type="checkbox" ng-model="selectAll" ng-click="checkAll()" />
</th>
<th></th>
<th>Sl no</th>
<th ng-click="orderByMe('name')">Name</th>
<th ng-click="orderByMe('country')">Country</th>
<th>Delete</th>
</tr>
<tr ng-repeat="x in names | orderBy:myOrderBy">
<td>
<input type="checkbox" ng-model="x.select" ng-click="xsetting(x)" />
</td>
<td>{{x.select}}</td>
<td>{{$index+1}}</td>
<td>{{x.name}}</td>
<td>{{x.country}}</td>
<td>
<button type="button" ng-click="Delete(x)">Delete</button>
</td>
</tr>
</table>
</div>
<script>
angular.module('myApp', []).controller('namesCtrl', ['$scope', 'filterFilter', function($scope, filterFilter) {
$scope.names = [{
name: 'Jani',
country: 'Norway'
}, {
name: 'Carl',
country: 'Sweden'
}, {
name: 'Margareth',
country: 'England'
}, {
name: 'Hege',
country: 'Norway'
}, {
name: 'Joe',
country: 'Denmark'
}, {
name: 'Gustav',
country: 'Sweden'
}, {
name: 'Birgit',
country: 'Denmark'
}, {
name: 'Mary',
country: 'England'
}, {
name: 'Kai',
country: 'Norway'
}];
//for sorting the rows
$scope.orderByMe = function(x) {
$scope.myOrderBy = x;
}
//single row deletion
$scope.Delete = function(x) {
$scope.names.splice($scope.names.indexOf(x), 1);
};
//selecting all checkboxes in the table
$scope.checkAll = function() {
angular.forEach($scope.names, function(x) {
x.select = $scope.selectAll;
});
};
//for selecting and deleting checked items
$scope.remove = function() {
$scope.names = filterFilter($scope.names, function(x) {
return !x.select;
});
};
}]);
</script>
</body>
</html>
,直到我到达我需要它的那个)
答案 0 :(得分:6)
由于type
位于“依赖范围”,您可以像这样访问它:
typename Base<T>::Type
然后应该像这样定义Alias
:
using Alias = typename Base<T>::Type;
请注意,此时编译器不知道Base<T>::type
是否描述了成员变量或嵌套类型,这就是为什么需要关键字typename
。
<强>层强>
您无需在每个图层重复此定义,以下是一个示例link:
template <typename T>
struct intermediate : Base<T>
{
// using Type = typename Base<T>::Type; // Not needed
};
template <typename T>
struct Derived : intermediate<T>
{
using Type = typename intermediate<T>::Type;
};
<强>更新强>
您也可以使用自己的类,这依赖于使用unknown specializations。
template <typename T>
struct Derived : Base<T>
{
using Type = typename Derived::Type; // <T> not required here.
};
答案 1 :(得分:4)
问题是Base<T>
是一个依赖的基类,并且可能存在特殊化,其中Type
不再被定义。比如你有一个像
template<>
class Base<int>
{}; // there's no more Type here
编译器无法事先知道(技术上它在模板实例化之前无法知道),特别是如果在不同的转换单元中定义了特化。因此,语言设计者选择采用简单的方法:每当你提到依赖的东西时,你需要明确地指明这一点,就像你的情况一样
using Alias = typename Base<T>::Type;
答案 2 :(得分:3)
我想这与编译器在查询参数T的特定实例化的上下文之外的Derived定义时无法检查是否可以从基类中提取Type有关。 / p>
是
在这种情况下,这是令人沮丧的,因为
Base
始终定义Type
而不管T
。
是
但总的来说,检测这是否属实是完全不可行的,如果语言的语义在真实时发生了变化,则会非常混乱。
也许这是有充分理由的。
C ++是一种通用的编程语言,而不是针对该程序优化的Smeeheey正在使用的编程语言。 :)
标准中规定这必须发生的规则是什么?
就是这样:
[C++14: 14.6.2/3]:
在类或类模板的定义中,如果基类依赖于模板参数,则在非限定名称查找期间不会检查基类范围。类模板或成员的定义点,或者在类模板或成员的实例化过程中。 [..]
对于这类问题,有什么好的解决方法?
你已经知道 - 资格:
using Alias = typename Base<T>::Type;
答案 3 :(得分:2)
定义模板时,有时事情需要更加明确:
using Alias = typename Base<T>::Type;
Rougly说:模板是各种各样的蓝图。在模板实例化之前,实际上什么都不存在。
在做同样的事情时,但在非模板上下文中,C ++编译器会试图找出Type
是什么。它会尝试在基类中找到它,并从那里开始。因为一切都已经宣布,而且事情几乎都是干涸的。
这里,在实例化模板之前,基类并不存在。如果您已经了解了模板特化,那么您应该意识到基类实际上可能没有Type
成员,当模板被实例化时,如果对后来定义的基类进行了专门化,这将覆盖整个事情,并将其内外转。
因此,当在这种情况下遇到一个普通的旧Type
时,编译器不能做出很多假设。它不能假设它可以查看任何已定义的模板基类,因为当事情开始凝固时,这些基类实际上可能看起来不像编译器认为它们看起来的样子;所以你明确地为编译器拼出所有内容,并在这里告诉编译器你正在尝试做什么。
答案 4 :(得分:2)
您不能在非推断的上下文中使用基类类型。 C ++拒绝假设未绑定的名称引用基类中的内容。
template <typename T>
struct Base {
using Type = int;
};
template<>
struct Base<int> {};
using Type=std::string;
template <typename T>
struct Derived : Base<T> {
using Alias = Type;
};
现在让我们看看这里发生了什么。 Type
中可以看到Derived
- 一个全球性的Derived
。 Type
应该使用与否吗?
根据&#34;在实例化之前解析任何内容&#34;,我们使用全局T
当且仅当int
为Base
时,由于{{1从中删除Type
的专门化。
遵循该规则我们遇到的问题是,我们可以在模板实例化之前基本上没有错误诊断,因为基类可以取代几乎任何东西的含义!致电abs
?可以成为父母的一员!提一个类型?可能来自父母!
这会强制模板基本上是宏;在实例化之前无法进行有意义的解析。轻微的拼写错误可能导致大量不同的行为。如果不创建测试实例,几乎任何模板中的不正确都无法诊断。
拥有可以检查正确性的模板意味着当您想要使用父类类型和成员时,您必须说您正在这样做。
答案 5 :(得分:0)
作为关于这一点的部分回应
[T]他很令人沮丧,因为
Base
始终定义Type
而不管T
。
我说:不,不是。
请考虑以下示例与您的不同之处仅在于Base<void>
的一行定义和Alias
的定义:
#include <iostream>
template <typename T>
struct Base
{
using Type = int;
};
template <typename T>
struct Derived : Base<T>
{
using Alias = typename Base<T>::Type; // error: no type named 'Type' in 'struct Base<void>'
};
template<> struct Base<void> {};
int main()
{
Derived<void>::Type b = 1;
std::cout << b << std::endl;
return 0;
}
在template <typename T> struct Derived : Base<T>
的上下文中,没有Type
存在的保证。您必须显式地告诉您的编译器,而Base<T>::Type
是一种类型(带typename
),如果您没有通过此合同,则最终会出现编译错误。