难以理解C ++依赖类型以及当前实例的内容

时间:2019-06-01 23:21:11

标签: c++ c++11 gcc clang

以下代码是根据此处的答案改编而成的: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;
}
如果我正确理解,show()中的

A行会告诉编译器使用当前实例化,因此我应该获得INT INT。在海湾合作委员会,我愿意。到目前为止,一切都很好。

如果我理解正确的话,B行应该告诉编译器考虑依赖类型,这会由于歧义而使该行出错。或者,如果这意味着 only 仅考虑依赖类型,则应该获取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

如果存在于基本类型上,则对于从当前实例中引用类型无效;仅在 that 类中定义了类型时才有效。即添加

typedef double dd;

D

然后

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

可以正常工作,的确如此。

此外,

typename D::sometype

似乎意味着 consider 依赖类型,但不是排他性的,因此,如果这种类型在当前实例化或依赖于模板参数的多个位置结束定义,则可能会出现错误。

但是,这再次假设Clang的行为根据规范是正确的,我无法与之交谈。


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

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

1 个答案:

答案 0 :(得分:0)

  

此外,

     

typename D::sometype

     

似乎意味着要考虑依赖类型

您是从哪里得到这个主意的? typename仅表示其后的不是数据成员,而是类型名称,因此可以完成模板的解析。您是否知道原始的C ++编译器过去是如何解析模板函数和类的?他们没有进行有意义的解析,只是吃掉了所有符号而仅进行{ / }平衡。是的,如果从未实例化模板定义,则几乎可以在模板定义中包括几乎所有垃圾!考虑到它是简单而肮脏的,但并不是那么愚蠢,因为当时的替代方案(正确解析)实际上并不可行。

为了在模板内部进行有意义的解析(甚至不解析很多名称),需要明确一些内容:无法解析的符号的类别(变量或函数,类型名称,模板名称)在实例化之前,像X * Y;X * (Y);X(Y);这样的简单东西是模棱两可的,并且不可解析(声明或表达式)。 因此,typename用于表示在模板定义时找不到的符号指定了类型,因此,如果Xtypename T::U,则所有这三个符号先前的同义词是声明;如果没有typename,并且如果T::U是从属的,则它们将被解析为表达式语句,并且在实例化模板时没有第二次解析,因此如果U实际上是一种类型,那将是一个错误

//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查找,在定义时仅考虑非依赖基类,然后:

  

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

因此,希望您“希望”实现的目标不应该发生!