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

Rob*_*son 6 c++ syntax templates visual-studio

虽然以下两个编译(使用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.
    }
};
Run Code Online (Sandbox Code Playgroud)

Bar*_*rry 2

这个问题依赖于名为“injected-class-name”的东西。来自[班级]

在看到类名后立即插入到声明它的范围中。类也被插入到类本身的范围中;这称为注入类名。出于访问检查的目的,注入的类名称被视为公共成员名称。

并来自 [temp.local]:

与普通(非模板)类一样,类模板有一个注入类名(第 9 条)。注入的类名可以用作模板名类型名当它与template-argument-list一起使用时,作为模板template-parameter模板参数,或者作为友元类模板声明的详细类型说明符中的最终标识符,它指的是类模板本身。否则,它相当于template-name后跟 中包含的类模板的template-parameters<>

也就是说,在定义Bar1<T>或中Bar2<T>,您可以使用Bar1Bar2来引用完整的类类型。也就是说,这些声明是等效的:

void Foo(Bar2<T> const & other);
void Foo(Bar2 const & other);
Run Code Online (Sandbox Code Playgroud)

但是,查找规则照常适用。虽然一个注入类名Base,但它是一个依赖名称,因此无法通过正常的非限定查找找到。来自 [temp.dep]:

在类或类模板的定义中,在类模板或成员的定义点或在类模板的实例化期间或在非限定名称查找期间,不会检查依赖基类 (14.6.2.1) 的范围。成员。

这使得:

Bar1() : Base() {}
Run Code Online (Sandbox Code Playgroud)

格式不正确。Base是不合格的查找,并且没有这样的名称Base。有一个Base<T>::Base(那里的注入类名),但该范围未经检查。您必须进行合格的查找:

Bar1() : Bar1<T>::Base() {}
Bar1() : Bar1::Base() { }
Run Code Online (Sandbox Code Playgroud)

或者不依赖注入的类名Base

Bar1() : Base<T>() { }
Run Code Online (Sandbox Code Playgroud)

VS 的接受是错误的Bar1Bar2如果比严格可能的更详细,那就完全没问题。没有错。

还值得注意的是,如果基础不依赖,您仍然可以使用它的注入类名称,即使它是一个模板:

template <class T> struct Base { };

struct Derived : Base<int> {
    Derived() : Base() { } // OK, lookup finds Base<int>::Base
};
Run Code Online (Sandbox Code Playgroud)