声明模板类成员和构造函数时的C ++惯用语

时间:2015-07-02 07:00:27

标签: c++ visual-studio templates syntax

虽然以下两个编译(使用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.
    }
};

2 个答案:

答案 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>中,您可以使用Bar1Bar2来引用完整的类类型。也就是说,这些声明是等效的:

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>>或其他)继承的话,会发生什么情况。它怎么能解决歧义?

第一种形式可行的想法可能源于规则的错误应用,在类模板定义中,您在提及类名时不需要提供模板参数。但这不适用于模板化基类。