【问题标题】:Using own class name to resolve type in a deduced context使用自己的类名来解析推断上下文中的类型
【发布时间】:2017-10-08 16:00:59
【问题描述】:

我使用我不熟悉的 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;
}

更新

如 cmets(underscore_d)中所述,Derived 类中不需要 &lt;T&gt;。也就是说,这完全没问题:

using Type = typename Derived::Type;

【问题讨论】:

  • 是的,这是合法的,参见例如en.cppreference.com/w/cpp/language/dependent_name 在“未知专业”下。
  • 似乎不需要模板参数&lt;T&gt;,我们可以简单地写成using Type = typename Derived::Type;,其中Derived总是引用带有模板参数的当前类类型'as if'。我错过了什么吗?我记得以前使用过这种模式,而不必指定模板参数。这也是定义明确的吗?
  • @underscore_d 你当然是对的,我想它是intermediate&lt;T&gt;... 留下的。我已经更新了问题。

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


【解决方案1】:

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

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

相比之下,在类级别,即使在没有限定名称的情况下使用这种类型声明也是可见的。因此,在实例化上述模板类时,在模板级别“不可见”的类型名称将在实例化类级别变得可见。

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

正如 n.m. 所指出的,他在评论中提供了this reference,类型定义坚持模板定义的观点,即使在实例级别会解析不同的类型:

在模板点查找和绑定非依赖名称 定义。即使在模板点,此绑定仍然有效 实例化有更好的匹配。

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

但是,由于子类的类型定义实际上被认为是一种新类型,它甚至可能以不同的方式定义,这可能不是有意的:

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;
}

所以子类不会“继承”而是“覆盖”各自的类型。 如果要“继承”,应该写:

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

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2019-04-26
    • 1970-01-01
    • 2016-07-12
    • 2016-09-24
    • 2022-01-23
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多