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

Trouble understanding C++ dependent types, vs what's on the current instantiation

本文关键字:实例化 C++ 类型 依赖      更新时间:2023-10-16

下面的代码改编自这里的答案: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) 在show()中,如果我理解正确,告诉编译器使用当前实例化,所以我应该得到 INT INT。 在海湾合作委员会,我愿意。目前为止,一切都好。

B行,同样,如果我理解正确的话,应该告诉编译器考虑依赖类型,由于歧义,这将使该行出错;或者,如果这意味着考虑依赖类型,我应该得到INT FLOAT。 在海湾合作委员会上,我也在那里得到INT INT。 为什么?


在 Clang 上运行这个。

A 行根本不编译。

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

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

它应该编译,还是这里的 Clang 是正确的?

B行确实在歧义上犯了错误

错误:在不同类型的多个基类中找到成员"result_type" 类型名 D::result_type R1


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

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

MyType::F

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

typedef double dd;

D

然后

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

show会工作得很好,事实确实如此。

此外

typename D::sometype

似乎意味着考虑依赖类型,但不完全考虑,因此如果此类类型最终在多个位置定义,无论是在当前实例化中还是依赖于模板参数,则会出现错误。

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


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

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

此外,

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 从"当前实例化"中查找,在定义时仅考虑非依赖基类,然后:

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

所以希望你"希望"的事情不应该发生!