使用自己的类名来解析推导出的上下文中的类型

时间:2017-05-10 06:34:12

标签: c++ c++11 templates language-lawyer

我使用我不熟悉的C ++结构回答了this个问题。我想知道这是合法的还是g ++(6.3.0)和clang ++(3.5.0)都是错误的。该示例可用online

#include <iostream>

template <typename T>
struct Base
{
    using Type = int;
};

template <typename T>
struct intermediate : Base<T>
{
    // 'Type' is not defined here, which is fine
};

template <typename T>
struct Derived : intermediate<T>
{
    using Type = typename Derived<T>::Type; // Is this legal?
//    using Type = typename intermediate<T>::Type; // Normal way of doing it
};

int main()
{
    Derived<void>::Type b = 1;
    std::cout << b << std::endl;
}

更新

正如评论中所提到的(underscore_d} <T>类中不需要Derived。也就是说,这非常好:

using Type = typename Derived::Type;

1 个答案:

答案 0 :(得分:7)

我花了一段时间来理解(非常好)的问题,所以让我首先解释一下我是如何理解这一点的:

在模板级别,如果此基类模板依赖于模板参数(例如,参见此answer),则不会检查基类模板的作用域。  因此,using Type = int在具有模板参数(如template <typename T> struct Base)的类模板中声明的类型对于派生自此基类模板(如template <typename T> struct intermediate : Base<T>)的类将不可见,除非使用限定名称,即typename Base<T>::Type)。

相反,在类级别,即使在没有限定名称的情况下使用此类型声明也是可见的。因此,在实例化上述模板类时,类型名称是&#34;不可见&#34;在模板级别将在已证实的类级别上可见。

因此,如果模板级别的子类将定义与基类模板具有相同(非限定)名称的类型,则在模板级别上这将不明显。  但是,在实例化模板时,这样的情况是否会导致(可能不匹配)重新定义类型,因为基于非限定类型名称的两种类型定义可能会在同一名称下变得可见?

正如n.m.在他的评论中提出this reference所指出的那样,类型定义在模板定义时坚持视图,即使在实例级别会解析另一种类型:

  

在模板点处查找并绑定非依赖名称   定义。即使在模板点,此绑定也成立   实例化有一个更好的匹配。

因此,它实际上是合法的,因为这不会导致类型的无效重新定义。

然而,作为子类&#39;类型定义实际上被认为是一种新类型,它甚至可能以不同的方式定义,这可能并非意图:

template <typename T>
struct Base
{
    using Type = int;
};

template <typename T>
struct intermediate : Base<T>
{
    // 'Type' is not defined here, which is fine
};

template <typename T>
struct Derived : intermediate<T>
{
    //using Type = typename Derived<T>::Type; // legal.
    using Type = const char*; // Legal, too!
};

int main()
{
    Derived<void>::Type b = "OK, but is this actually intended?";
    std::cout << b << std::endl;
}

所以子类不会继承&#34;但是&#34;覆盖&#34;各自的类型。 如果有人想要继承&#34;,应该写:

struct Derived : intermediate<T>
{
    using typename intermediate<T>::Type; // Inherits the type.
};