9

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]

类或命名空间成员或枚举器的名称可以在应用于表示其类、命名空间或枚举的嵌套名称说明符的 :: 范围解析运算符 (5.1) 之后引用。如果嵌套名称说明符中的 :: 范围解析运算符前面没有 decltype 说明符,则查找 :: 之前的名称仅考虑其特化为 types 的名称空间、类型和模板。如果找到的名称没有指定命名空间或类、枚举或依赖类型,则程序是非良构的。

14.2 模板特化的名称 [temp.names]

对于模板参数显式限定的模板名称,必须知道该名称以引用模板

在名称查找 (3.4) 发现名称是模板名称或 operator-function-id 或literal-operator-id 引用一组重载函数后,其中任何成员都是函数模板,如果后面跟着a <, the<总是作为模板参数列表的分隔符,从不作为小于运算符

编辑

为避免将此问题与表达式和声明之间的歧义混淆,这里是原始代码,其中模板使用类型参数而不是非类型参数。

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,并在稍后解决歧义。标准是否允许实施者这样做?

4

2 回答 2

8

问题是您引用的段落最终应用得太晚了。在到达那里之前,编译器必须确定 in ambiguous<3>::value<and>是模板参数分隔符,而不是大于和小于。(考虑:

int ambiguous;
int value:
//  ...
int i = ambiguous<3>::value;

,它解析为(ambiguous < 3) > ::value,其中<>分别小于和大于。)这涉及到ambiguous作为非限定名称的查找,并将符号绑定到N::ambiguous。之后,您会被N::ambiguous<3>左侧 的实例化模板困住::,这是不合法的。

编辑:

这个问题并不像人们想象的那么清楚:该标准仅在第 14.2 节中提到了第 3.4 节,其中讨论了这一点,第 3.4 节讨论了名称查找的所有可能规则。另一方面,实际上只有一种方法可以解释它:编译器在知道是否命名模板之前无法ambiguous进一步解析任何内容,并且可以决定以下<是否大于或打开模板参数列表。当然,它不能在以后“重新绑定”参数,一旦它解析了以下标记,因为在一般情况下,重新绑定可能会改变 <,使解析无效。在实践中,尽管标准并没有像它应该的那样清楚地说明,但在这种情况下,名称查找必须是非限定名称查找(或类成员访问,如果名称前面有 a.->运算符)。

于 2013-08-19T10:40:37.677 回答
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;
}
于 2013-08-19T10:43:52.563 回答