虽然以下两个编译(使用Visual Studio 2013),对于C ++习语来说,其中一个更“正确”吗?在调用基类构造函数和声明成员时,我特别谈到显式模板参数。标准是否对此有看法?是否有一个很好的实际理由来选择一个而不是另一个?
template<class T>
class Bar1 : public Base<T>
{
public:
Bar1() : Base() {}
Bar1(T value) : Base(value) {}
Bar1(Bar1 const & other) : Base(other.value) {}
void Foo(Bar1 const & other)
{
// Some foo related activity.
}
};
template<class T>
class Bar2 : public Base<T>
{
public:
Bar2() : Base<T>() {}
Bar2(T value) : Base<T>(value) {}
Bar2(Bar2<T> const & other) : Base<T>(other.value) {}
void Foo(Bar2<T> const & other)
{
// Some foo related activity.
}
};
答案 0 :(得分:2)
这个问题依赖于名为 inject-class-name 的东西。来自[班级]
class-name 被插入到在看到 class-name 之后立即声明它的范围内。 class-name 也会插入到类本身的范围内;这被称为 inject-class-name 。 出于访问检查的目的, inject-class-name 被视为公共成员名称。
来自[temp.local]:
与普通(非模板)类一样,类模板具有 inject-class-name (第9条)。 inject-class-name 可用作模板名称或类型名称。当它与 template-argument-list 一起使用时, 作为模板模板参数的模板参数,或作为朋友类模板的 elaborated-type-specifier 中的最终标识符声明,它指的是类模板本身。否则,它是等价的 到 template-name 后跟
<>
中包含的类模板的 template-parameters 。
也就是说,在定义Bar1<T>
或Bar2<T>
中,您可以使用Bar1
或Bar2
来引用完整的类类型。也就是说,这些声明是等效的:
void Foo(Bar2<T> const & other);
void Foo(Bar2 const & other);
但是,查找规则正常应用。虽然是 Base
的注入类名,但这是一个从属名称,因此无法通过正常的非限定查找找到。来自[temp.dep]:
在类或类模板的定义中,不检查依赖基类(14.6.2.1)的范围 在非限定名称查找期间,无论是在类模板或成员的定义点,还是在类模板或成员的实例化过程中。
这使得:
Bar1() : Base() {}
形成不良。 Base
是不合格的查找,并且没有此类名称Base
。有Base<T>::Base
(注入类名称),但该范围未经审查。您必须要么进行合格的查找:
Bar1() : Bar1<T>::Base() {}
Bar1() : Bar1::Base() { }
或不依赖于注入类名Base
:
Bar1() : Base<T>() { }
VS接受Bar1
是错误的。 Bar2
完全没问题,如果比严格可能更冗长。没有错。
值得注意的是,如果基础不依赖,即使它是模板,你仍然可以使用它的注入类名:
template <class T> struct Base { };
struct Derived : Base<int> {
Derived() : Base() { } // OK, lookup finds Base<int>::Base
};
答案 1 :(得分:0)
第一种形式不正确,不应该编译。要了解原因,请考虑如果您从Base<Foo>
和Base<Bar>
(或Base<T>
和Base<Base<T>>
或其他)继承的话,会发生什么情况。它怎么能解决歧义?
第一种形式可行的想法可能源于规则的错误应用,在类模板定义中,您在提及类名时不需要提供模板参数。但这不适用于模板化基类。