为什么这样做:
template <typename A>
struct S {
A a;
template <typename B>
auto f(B b) ->
decltype(a.f(b))
{
}
};
但这不是(a
和f
交换位置):
template <typename A>
struct S {
template <typename B>
auto f(B b) ->
decltype(a.f(b))
{
}
A a;
};
说a
未在该范围内声明(在decltype内),但添加显式this->
使其有效。
答案 0 :(得分:4)
template <typename A>
struct S {
A a;
template <typename B>
auto f(B b) ->
decltype(a.f(b))
{
}
};
这是有效的,因为在尾随返回类型中,周围类的成员是可见的。并非所有成员,只有在它之前声明的成员(在尾随返回类型中,类不被认为是完整的,而不是函数体)。那么这里做了什么:
a
是否依赖。由于在a
之前声明了f
,因此发现a
引用了一个类成员。 通过C ++中的模板规则,发现a
引用当前实例化的成员,因为它是周围模板的实例化的成员。在C ++中,这个概念主要用于决定名称是否依赖:如果已知名称引用周围模板的成员,则在实例化时不一定需要查找,因为编译器已经知道模板的代码(用作从中实例化的类类型的基础!)。考虑:
template<typename T>
struct A {
typedef int type;
void f() {
type x;
A<T>::type y;
}
};
在C ++ 03中,声明y
的第二行会出错,因为A<T>::type
是一个从属名称,前面需要typename
。只有第一行被接受了。在C ++ 11中,这种不一致性是固定的,两个类型名称都是非依赖的,不需要typename
。如果您将typedef更改为typedef T type;
,则两个声明x
和y
都将使用依赖类型,但 都不需要typename
,因为您仍然命名当前实例化的成员,并且编译器知道您为类型命名。
a
是当前实例化的成员。但它是依赖的,因为用于声明它的类型(A
)是依赖的。但是,这在您的代码中并不重要。无论是否依赖,都会找到a
并且代码有效。 template <typename A>
struct S {
template <typename B>
auto f(B b) ->
decltype(a.f(b))
{
}
A a;
};
在此代码中,再次查找a
以查看它是否依赖和/或它是否是当前实例化的成员。但是,由于我们上面已经了解到在尾随返回类型之后声明的成员不可见,我们无法找到a
的声明。在C ++中,除了当前实例化的概念#34;还有另一个概念:
未知专业化的成员。此概念用于指代名称可能改为引用依赖于模板参数的类成员的情况。如果我们访问了B::a
,则a
将成为未知专业化的成员,因为 unknown 当B
替换为a
时,将显示哪些声明实例。
既不是当前成员,也不是未知专业成员。所有其他名称都是如此。 您的案例适合,因为已知a
在实例化发生时永远不能成为任何实例化的成员(请记住名称查找无法找到f
,因为它已声明在a
之后。
由于template <typename A>
struct S {
template <typename B>
auto f(B b) ->
decltype(this->a.f(b))
{
}
A a;
};
不依赖于任何规则,因此未找到任何声明的查找是绑定,这意味着在实例化中没有可以找到声明的其他查找。在模板定义时查找非依赖名称。现在GCC正确地给你一个错误(但请注意,一如既往,不需要立即诊断出错误的模板。)
this
在这种情况下,您添加了a
并接受了GCC。再次跟随this->
的名称S
是查找它是否可能是当前实例化的成员。但是,由于尾随返回类型中的成员可见性,因此未找到任何声明。因此,该名称被视为不是当前实例化的成员。由于在实例化时没有办法,a
可以有S
可以匹配的其他成员(没有this->a
的基类依赖于模板参数),名称也不是未知专业化的成员。
C ++再一次没有让this->
依赖的规则。但是它使用S
,因此名称必须在实例化时引用a
的某个成员!所以C ++标准说
类似地,如果对象表达式的类型是当前实例化的类成员访问表达式中的id-expression不引用当前实例化的成员或未知专业化的成员,则程序生病 - 即使没有实例化包含成员访问表达式的模板;无需诊断。
此代码不需要诊断(GCC实际上也没有给出它)。成员访问表达式this->a
中的id-expression decltype
依赖于C ++ 03,因为该标准中的规则没有像C ++ 11中那样详细和精细。暂时让我们想象C ++ 03有this->a
和尾随返回类型。这意味着什么?
S<SomeClass>
将依赖this->a
的实例化查找将失败,因为在实例化时不会找到{{1}}(正如我们所说,尾随返回类型不会看到稍后声明的成员)。 因此,C ++ 11对该代码的早期拒绝是好的和有用的。
答案 1 :(得分:2)
成员函数的主体被编译为好像它是在类之后定义的。因此,在该类中声明的所有内容都在此范围内。
但是,函数的声明仍然在类声明中,并且只能看到它之前的名称。
template <typename A>
struct S {
template <typename B>
auto f(B b) ->
decltype(a.f(b)); // error - a is not visible here
A a;
};
template <typename A>
template <typename B>
auto S<A>::f(B b) ->
decltype(a.f(b))
{
return a.f(b); // a is visible here
}
答案 2 :(得分:2)
标准说(第14.6.2.1节):
如果对于给定的一组模板参数,实例化了一个模板的特化,该模板的特殊化引用了具有qualified-id或类成员访问表达式的当前实例化的成员,那么在qualified-id或class成员中的名称在模板实例化上下文中查找访问表达式。
this->a
是类成员访问表达式,因此应用此规则并在实例化时进行查找,其中 。S<A>
已完成
最后,这根本不能解决您的问题,因为第5.1.1节说:
如果声明声明了类X的成员函数或成员函数模板,表达式
this
是类型为“指向 cv-qualifier-seq {的指针的prvalue {1}}“在可选的 cv-qualifier-seq 与功能定义的末尾,成员声明符或声明符 即可。它不会出现在可选的 cv-qualifier-seq 之前 它不应出现在静态成员函数的声明中(尽管它的类型和值类别是在静态成员函数中定义的,因为它们在非静态成员函数中)。
所以你不能在这里使用X
,因为它位于函数声明的 cv-qualifier-seq 部分之前。
等等,不,不是!第8.4.1节说
function-definition 中的声明者应具有
形式
this->
D1 (
parameter-declaration-clause
)