首页 > 解决方案 > 难以理解 C++ 依赖类型,以及当前实例化的内容

问题描述

下面的代码改编自这里的答案:https ://stackoverflow.com/a/17579889/352552

我提出这个问题的目的是试图更好地理解 C++ 如何处理依赖类型的类型解析,而不是当前实例化的类型,因此不需要typename限定符。我从不同的编译器得到了相互矛盾的结果,所以我来这里寻找更规范的答案。

考虑这段代码

#include <iostream>

struct B {
  typedef int result_type;
};

template<typename T>
struct C {
};

template<>
struct C<float> {
  typedef float result_type;
}; 

template<typename T>
struct D : B, C<T> {
  std::string show() {
    //A) Default to current instantiation - ignore dependent type, even if one exists, or so I hope
    D::result_type r1;

    //B) What **exactly** does typename add, here?
    //typename D::result_type r1;

    return whichType(r1);
  }

  std::string whichType (int val){
    return "INT";
  }
  std::string whichType (float val){
    return "FLOAT";
  }    
};


int main() {  
  D<std::string> stringD;
  D<float> floatD;
  std::cout<<"String initialization "<<stringD.show()<<std::endl;
  std::cout<<"Float initialization "<<floatD.show()<<std::endl;
}

如果我理解正确,行 A) inshow()告诉编译器使用当前实例化,所以我应该得到 INT INT。在 GCC 上,我愿意。到目前为止,一切都很好。

再次,如果我理解正确的话,B 行应该告诉编译器考虑依赖类型,这会因为歧义而导致该行错误;或者,如果这意味着考虑依赖类型,我应该得到 INT FLOAT。在 GCC 上,我也在那里得到 INT INT 。为什么?


在 Clang 上运行它。

A 行根本无法编译。

错误:“D”中没有名为“result_type”的类型;你的意思是简单的'result_type'吗?D::result_type r1;

放弃D::确实会产生 INT INT。

它应该已经编译,还是 Clang 在这里正确?

B行确实在歧义上出错

错误:在不同类型类型名称 D::result_type r1 的多个基类中找到成员“result_type”


这里的任何人都可以权威地说哪个编译器(如果有的话!)是规范正确的,为什么?

假设 Clang 是正确的,它可能意味着

MyType::F

如果它存在于基类型上,则从当前实例化中引用类型是无效的;只有在该类上定义了类型时它才有效。即添加

typedef double dd;

D

接着

D::dd d = 1.1;
std::cout<<d;

inshow可以正常工作,确实如此。

而且,

typename D::sometype

似乎意味着考虑依赖类型,但并非排他性的,因此如果这种类型最终在多个地方定义,无论是在当前实例化中,还是依赖于模板参数,都会出现错误。

但同样,这一切都假设 Clang 的行为根据规范是正确的,我无法与之交谈。


链接到我正在使用的 GCC repl: https ://wandbox.org/

链接到我正在使用的 Clang repl: https ://repl.it/languages/cpp11

标签: c++c++11gccclang

解决方案


而且,

typename D::sometype

似乎意味着考虑依赖类型

你从哪里得到这个想法的?typenameonly 表示后面的不是数据成员而是类型名,这样就可以解析模板了。你知道过去原始的 C++ 编译器是如何解析模板函数和类的吗?他们没有进行有意义的解析,他们只是吃了所有符号,只做{/}平衡。是的,如果它们从未被实例化,你可以在模板定义中包含几乎所有垃圾!它既简单又肮脏,但如果你仔细想想,它并不是那么空洞,因为当时的替代方案(正确解析)并不是真正可行的。

为了在模板中进行有意义的解析(甚至不解析许多名称),需要明确一些事情:在实例化之前无法解析的符号类别(变量或函数、类型名称、模板名称),像X * Y;,X * (Y);和这样简单的东西X(Y);是模棱两可且不可解析的(声明或表达式)。Sotypename用于表示在模板定义时找不到的符号指定一个类型,所以如果X是,typename T::U那么前面的三个句法都是声明;如果没有typenameand ifT::U依赖,它们将被解析为表达式语句,并且在实例化模板时没有第二次解析,因此 ifU实际上是一种类型,这将是一个错误。

//A) Default to current instantiation - ignore dependent type, even if one exists, or so I hope
D::result_type r1;

根据https://en.cppreference.com/w/cpp/language/dependent_name的“当前实例化”查找在定义时只考虑非依赖基类,然后:

如果当前实例化成员的查找在实例化点和定义点之间给出不同的结果,则查找是不明确的。

所以希望你“希望”的事情不会发生!


推荐阅读