下面的代码改编自此处的答案: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。在 GCC 上,我这样做。到目前为止,一切都很好。
B 行,同样,如果我理解正确的话,应该告诉编译器考虑 依赖类型,这会使该行由于歧义而出错;或者,如果这意味着只考虑依赖类型,我应该得到 INT FLOAT。在 GCC 上,我也在那里得到 INT INT。为什么?
在 Clang 上运行它。
A 行根本无法编译。
error: no type named 'result_type' in 'D'; did you mean simply 'result_type'? D::result_type r1;
删除 D::
确实会产生 INT INT。
它应该已经编译,还是 Clang 在这里是正确的?
B行确实在歧义上出错
error: member 'result_type' found in multiple base classes of different types typename D::result_type r1
这里有谁能权威地说出哪个编译器(如果有的话!)是规范正确的,为什么?
假设 Clang 是正确的,它可能意味着
MyType::F
如果存在于基类型上,则对于从当前实例化中引用类型是无效的;只有在 that 类上定义类型时才有效。即添加
typedef 双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
最佳答案
Moreover,
typename D::sometype
seems to mean consider dependent types
你从哪里得到这个想法的? typename
只是表示后面不是数据成员而是类型名,这样就可以解析模板了。你知道原始的 C++ 编译器在过去是如何解析模板函数和类的吗?他们没有做任何有意义的解析,他们只是吃了所有的符号,只做 {
/}
平衡。是的,在某些时候你可以在模板定义中包含几乎所有垃圾,如果它们从未被实例化的话!它既简单又肮脏,但仔细想想也不是那么空洞,因为替代方案(正确的解析)在当时并不切实可行。
为了在模板中更有意义地解析(甚至不解析许多名称)一些事情需要明确:无法解析的符号的类别(变量或函数、类型名称、模板名称)在实例化之前,像 X * Y;
、X * (Y);
和 X(Y);
这样简单的东西是模棱两可的,不可解析(声明或表达)。 所以typename
用来表示一个在模板定义时找不到的符号指定一个类型,所以如果X
是typename 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从“当前实例化”进行的查找仅考虑定义时的非依赖基类,然后:
If the lookup of a member of current instantiation gives a different result between the point of instantiation and the point of definition, the lookup is ambiguous.
希望您“希望”的事情不会发生!
关于c++ - 难以理解 C++ 依赖类型,以及当前实例化的内容,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/56411114/