在查找:::之前的名称时,是否应该可以看到函数模板的名称?

时间:2013-08-19 10:24:55

标签: c++ templates language-lawyer

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,并在以后解决模糊性。标准是否可以帮助实现者这样做?

2 个答案:

答案 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;
}