【问题标题】:CRTP with dependent type, type name lookup具有依赖类型的 CRTP,类型名称查找
【发布时间】:2021-01-08 06:55:51
【问题描述】:

关于派生类中的嵌套类型不应在 CRTP 中使用的讨论很多。但是,以下一些情况实际上可以正常工作:

template <class T>
class Base {
 public:
  int value = T().value;
};

template <class T>
class B {
 public:

  // case 1: ok, as long as class C does not instantiate;
  class C : public T::I {};

  // case 2: bad, class C instantiated;
  // class C : public T::I {};
  // C c;

  // case 3: bad,
  // typedef typename T::I TI;

  // case 4: bad, similarly, even T::I is used as template parameter
  // typedef Base<typename T::I> BaseTI;

  // case 5: if used as function parameter type, make a trivial template 
  template <typename R, 
            typename = std::enable_if_t<std::is_same_v<T, typename R>::type>>>
  auto get(R n) { return n; }
};

class D : public B<D> {
 public:
  class I {
   public:
    int value;
  };
  I i;
};

我认为 2、3、4 失败的原因是编译器的 2 阶段名称查找;我也可以推断案例 5 是可以的,因为只有当 B::&lt;T&gt;::get&lt;R&gt; 被调用时,它才会得到解决。但是,我不明白为什么案例 1 运行良好,尤其是给定的案例 2 无法编译。

为什么 1 和 2 不同?

【问题讨论】:

    标签: c++ crtp dependent-type


    【解决方案1】:

    当您的编译器看到class D : public B&lt;D&gt; 时,它会尝试实例化B&lt;D&gt;。此时,D 仍然是不完整的类型,I 尚不可知。然而,由于“延迟实例化”,嵌套类在第一次使用之前不会被实例化。来自这个answer

    ...其成员定义的实例化被推迟到它们被实际使用。这不仅适用于成员函数,也适用于静态数据成员和嵌套类

    因此案例 2 失败,但案例 1 工作正常。从引用的答案中,您还可以看到 typedef 没有被延迟,因此,D 在您声明它们时是不完整的。

    【讨论】:

    • 尝试了静态数据案例。但是,const static int i = T::I().value;const static typename T::I ti; 仍然给出相同的错误 `error: no member named 'I' in 'D'`;看起来即使静态数据被实例化了;它的类型被查到了吗?
    • 我想我现在可能明白了:它只指静态variable 实例化被延迟,而不是它的类型(因此类型查找);同样,对于成员函数,它的定义没有被检查(因此 T::I 可以出现在函数定义体中,但不会出现在其签名中)l 但是,我并不完全理解,为什么嵌套类,class C : public T::I,其中声明C 派生自T::IT::I 未被检查/查找?
    • @dragonxlwang 我也不是这方面的专家,但我认为public T::I 部分已经属于类定义(想想你将如何转发声明一个类)。与函数一样,仅检查声明,但延迟检查定义部分。其他人可以证实这一点吗?还是我走错了路?
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-01-12
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多