这个所谓的重复解释了为什么不允许这样做的机制,并显示了它无法工作的极端情况,但未能解决为什么 C++ 在它可以工作的情况下拒绝允许它的问题。
tl;dr;我知道C++不能做到这一点,并且我知道如何通过ADL解决它并且我不会问这个。我不知道的是为什么语言设计选择(或被迫)不允许这样做。
以下代码适用于除最后一种情况之外的所有情况:
template <class T>
struct A { struct R {}; };
template <>
struct A<double> { using R = int; };
void Fn(int);
template <class T>
void Fn(typename A<T>::R v);
////////////////////////////////
void Test() {
A<double>::R d;
A<int>::R v;
Fn(0); // Works; calls Fn(int)
Fn(d); // Wroks; calls Fn(int)
Fn(v); // Fails; expected to call Fn<int>(A<int>::R)
}
它失败了,因为语言无法为
T
的模板重载推断模板参数 Fn
,以便将 typename A<T>::R
变成 A<int>::R
。对于人类来说,在这种情况下,很明显 T
应该是 int
,但 C++ 的规则不允许做出这样的推论。
是什么促使或迫使 C++ 的设计者施加这种限制?
我怀疑答案是,在实际解决类模板之前“猜测”类模板将具有嵌套类型在技术或哲学上是有问题的,或者在现有编译器中实现该逻辑在架构上是禁止的。也就是说,只有第一个问题是一个基本问题,我实际上并不明白为什么其中任何一个都一定是正确的,这表明我错过了一些有趣的东西。 顺便说一句:除非 Bjarne Stroustrup 或 90 年代 C++ 标准委员会的某个人看到了这一点,否则我得到的最佳答案可能就是消息灵通的猜测。
附注
A<R>::S
using
别名提供时(特别是通过显式专业化),生成的正确/完全解析的类型可能根本不会提及
A<>
。虽然有道理,但这不是我要问的情况。
template<int I> struct Int;
template<> struct Int<0> { using T = int; };
template<> struct Int<1> { using T = long; };
template<int I>
struct C {
using T = typename Int<I % 2>::T;
static int Mine();
friend int Get(T) { return C::Mine(); }
};
void Test() {
C<0> c0; // Ok
C<1> c1; // This doesn't break anything.
C<2> c2; // Delete this and things work.
/*
error: redefinition of 'int Get(C<2>::T)'
note: 'int Get(C<0>::T)' previously declared here
*/
}
T
→typename A<T>::R
。如果确实存在具有该名称的嵌套类型,那么当然可以推断出它;据推测,这种情况被认为太狭隘而无法激励,特别是如果用户可能不理解该功能的局限性和脆弱性。
当提出支持可行案例的论文时,委员会的感觉是这将是一个“突破性”的改变,在某种程度上不寻常的意义上,它将允许演绎(及其相关的内省权力)适用于以前不受此类分析影响的类型。人们不想重命名他们的嵌套类型(为了源兼容性而破坏 ABI,甚至在旧名称下给定类型别名)以防止语义发生变化,以及为任何专业化的成员执行诸如
customize 之类的操作的能力类模板(通过重载或部分特化)不被认为是引人注目的。