clang和gcc都拒绝这段代码:
template<int i>
struct ambiguous
{
static const int value = i;
};
namespace N
{
template<int i>
void ambiguous();
int i = ambiguous<3>::value; // finds the function template name
}
但是,他们都接受以下代码:
struct ambiguous
{
static const int value = 0;
};
namespace N
{
void ambiguous();
int i = ambiguous::value;
}
标准规定,::
之前的名称的名称查找仅考虑其专业化类型的名称空间,类型和模板“。 clang和gcc是否正确拒绝此代码?如果是这样,我错过了什么?
来自C ++工作草案标准n3337
3.4.3合格名称查找[basic.lookup.qual]
可以在:: scope解析之后引用类或命名空间成员或枚举器的名称 operator(5.1)应用于表示其类,命名空间或枚举的嵌套名称说明符。如果一个 :: 嵌套名称说明符中的作用域解析运算符前面没有decltype-specifier,查找 在此之前的名称::仅考虑其专业化类型为的名称空间,类型和模板。 如果找到的名称未指定命名空间或类,枚举或依赖类型,则为程序 是不正确的。
14.2模板专精的名称[temp.names]
对于要由模板参数显式限定的模板名称,必须知道该名称才能引用 到模板。
名称查找(3.4)后发现名称是模板名称或者operator-function-id或literal-operator-id引用一组重载函数的任何成员这是一个功能模板如果是这样的话 后跟
<
,<
始终作为模板参数列表的分隔符,而不是小于 操作
为了避免将此问题与表达式和声明之间的歧义混淆,这里是使用类型参数而不是非类型参数的模板的原始代码。
template<class>
struct ambiguous
{
static const int value = 0;
};
namespace N
{
template<class>
void ambiguous();
int i = ambiguous<int>::value; // finds the function template name
}
在所有情况下都会导致相同的错误。 <
不能解释为运算符。
ambiguous
明确地是模板名称,但可以是类型或函数。可以在不知道它是否命名函数或类型的情况下解析整个模板ID,并在以后解决模糊性。标准是否可以帮助实现者这样做?
答案 0 :(得分:8)
问题是你引用的段落最终会成为
应用太晚了。在到达之前,编译器必须
确定ambiguous<3>::value
中<
和>
是int ambiguous;
int value:
// ...
int i = ambiguous<3>::value;
模板参数分隔符,并且不大于和小于
比。 (考虑一下:
(ambiguous < 3) > ::value
,解析为<
,>
和。ambiguous
N::ambiguous
分别小于和大于。)这
涉及查找N::ambiguous<3>
作为非限定名称,以及
将符号绑定到::
。之后,你被困住了
使用左侧的实例化模板ambiguous
<
,这是不合法的。
这个问题并不像人们所希望的那样清晰:仅限标准
是指第14.2节中的第3.4节,其中讨论了这一点,
第3.4节讨论了所有可能的名称规则
抬头。另一方面,实际上只有一种方法
解释它:编译器不能解析任何事情,直到
它知道<
是否命名模板,并且可以
确定以下.
是否大于或打开
模板参数列表。当然,它不能“重新绑定”
稍后,一旦它解析了以下标记,从那以后
在一般情况下,重新绑定可能会改变意义
->
,使解析无效。在实践中,虽然标准
没有尽可能清楚地说明,名称查找
在这种情况下,必须是非限定名称查找(或类成员
如果名称前面有{{1}}或{{1}}运算符,则可以访问。
答案 1 :(得分:3)
内部命名空间中的似乎模板隐藏了外部名称。
template<int i>
struct A
{
static const int value = 0;
};
struct B
{
static const int value = 0;
};
typedef A<0> C;
namespace N
{
// The local function A is not declaced, yet, but the global A is:
int early_e = A<3>::value; // Ok: The A in the global namespace. [simple-template-id}
template<int i>
int A() { return 0; }
int B() { return 0; }
int C() { return 0; }
int a = A<3>(); // Ok: The A in the namespace. [simple-template-id}
int b = N::A<3>(); // Ok: The A in the namespace. [N::simple-template-id]
int c = ::N::A<3>(); // Ok: The A in the namespace. [::N::simple-template-id]
int d = ::A<3>::value; // Ok: The A in the global namespace. ::simple-template-id::identifier]
// The local function A is no type: "templates whose specializations are types"
int e = A<3>::value; // Error: The namespace has the function name A,
// which hides the global A. [simple-template-id::identifier]
// The local function B is no type, but the global B is a type:
int f = B::value; // Ok: The B in the global namespace. [class-name::identifier]
// The local function C is no type, but the global typedef C is a type:
int g = C::value; // Ok: The C in the global namespace. [typedef-name::identifier]
}
int main() {
return 0;
}