gpt4 book ai didi

c++ - operator[] 查找模板基类

转载 作者:IT老高 更新时间:2023-10-28 12:54:42 28 4
gpt4 key购买 nike

下面的代码让我们有点头疼:clangMSVC 接受下面的代码,而 GCC 拒绝它。我们相信这次 GCC 是正确的,但我想在提交错误报告之前确认一下。那么,对于 operator[] 查找有什么我不知道的特殊规则吗?

struct X{};
struct Y{};

template<typename T>
struct B
{
void f(X) { }
void operator[](X){}
};

template<typename T>
struct C
{
void f(Y) { }
void operator[](Y){}
};

template<typename T> struct D : B<T>, C<T> {};

int main()
{
D<float> d;
//d.f(X()); //This is erroneous in all compilers
d[Y()];//this is accepted by clang and MSVC
}

那么上面的代码在解析main函数中的operator[]调用是否正确?

最佳答案

问题出在哪个编译器上并不是 100% 清楚的。该标准涵盖了许多名称查找规则(这是一个问题),但更具体地说,第 13.5.5 节涵盖了 operator[] 重载:

13.5.5 Subscripting [over.sub]

1 - operator[] shall be a non-static member function with exactly one parameter. It implements the subscripting syntax

postfix-expression [ expr-or-braced-init-list ]

Thus, a subscripting expression x[y] is interpreted as x.operator[](y) for a class object x of type T if T::operator[](T1) exists and if the operator is selected as the best match function by the overload resolution mechanism (13.3.3).

查看关于重载的标准(第 13 章):

13 Overloading [over]

1 - When two or more different declarations are specified for a single name in the same scope, that name is said to be overloaded. By extension, two declarations in the same scope that declare the same name but with different types are called overloaded declarations. Only function and function template declarations can be overloaded; variable and type declarations cannot be overloaded.

2 - When an overloaded function name is used in a call, which overloaded function declaration is being referenced is determined by comparing the types of the arguments at the point of use with the types of the parameters in the overloaded declarations that are visible at the point of use. This function selection process is called overload resolution and is defined in 13.3.

...

13.2 Declaration matching [over.dcl]

1 - Two function declarations of the same name refer to the same function if they are in the same scope and have equivalent parameter declarations (13.1). A function member of a derived class is not in the same scope as a function member of the same name in a base class.

所以根据这个和关于派生类的第 10.2 节,因为你已经声明了 struct D : B, CBCoperator[] 的成员函数,但类型不同,因此 operator[] 函数在 D 范围内被重载(因为没有using 也不是 operator[] 被直接覆盖或隐藏在 D 中)。

基于此,MSVC 和 Clang 的实现是不正确的,因为 d[Y()] 应该 被评估为 d.operator[](Y()),这将产生一个模棱两可的名称解析;所以问题是为什么他们完全接受d[Y()]的语法

我能看到的关于下标 ([]) 语法的唯一其他区域引用了 5.2.1 部分(其中说明了下标表达式是什么)和 13.5.5(如上所述),这意味着那些编译器正在使用其他规则来进一步编译 d[Y()] 表达式。

如果我们查看名称查找,我们会看到 3.4.1 非限定名称查找第 3 段指出

The lookup for an unqualified name used as the postfix-expression of a function call is described in 3.4.2.

其中 3.4.2 规定:

3.4.2 Argument-dependent name lookup [basic.lookup.argdep]

1 - When the postfix-expression in a function call (5.2.2) is an unqualified-id, other namespaces not considered during the usual unqualified lookup (3.4.1) may be searched, and in those namespaces, namespace-scope friend function or function template declarations (11.3) not otherwise visible may be found.

2 - For each argument type T in the function call, there is a set of zero or more associated namespaces and a set of zero or more associated classes to be considered. The sets of namespaces and classes is determined entirely by the types of the function arguments (and the namespace of any template template argument). Typedef names and using-declarations used to specify the types do not contribute to this set. The sets of namespaces and classes are determined in the following way:

...

(2.2) - If T is a class type (including unions), its associated classes are: the class itself; the class of which it is a member, if any; and its direct and indirect base classes. Its associated namespaces are the innermost enclosing namespaces of its associated classes. Furthermore, if T is a class template specialization, its associated namespaces and classes also include: the namespaces and classes associated with the types of the template arguments provided for template type parameters (excluding template template parameters); the namespaces of which any template template arguments are members; and the classes of which any member templates used as template template arguments are members. [ Note: Non-type template arguments do not contribute to the set of associated namespaces.—end note ]

注意对可能的强调。

根据以上几点以及 3.4 中的其他几点(名称查找),可以相信 Clang 和 MSVC 正在使用这些规则首先查找 d[](因此将其查找为 C::operator[]) 与使用 13.5.5 将 d[] 转换为 d.operator[] 并继续编译。

应该注意的是,将基类的运算符带入 D 类的范围或使用显式范围确实可以在所有三个编译器中“修复”这个问题(正如预期的那样关于引用中的 using 声明子句),例如:

struct X{};
struct Y{};

template<typename T>
struct B
{
void f(X) { }
void operator[](X) {}
};

template<typename T>
struct C
{
void f(Y) { }
void operator[](Y) {}
};

template<typename T>
struct D : B<T>, C<T>
{
using B<T>::operator[];
using C<T>::operator[];
};

int main()
{
D<float> d;

d.B<float>::operator[](X()); // OK
//d.B<float>::operator[](Y()); // Error

//d.C<float>::operator[](X()); // Error
d.C<float>::operator[](Y()); // OK

d[Y()]; // calls C<T>::operator[](Y)
return 0;
}

由于标准最终留给实现者解释,我不确定在这种情况下哪个编译器在技术上是正确的,因为 MSVC 和 Clang 可能 正在使用其他规则来编译它,鉴于标准中的下标段落,我倾向于说他们没有像 GCC 在这种情况下那样严格遵守标准。

我希望这可以增加对问题的一些洞察力。

关于c++ - operator[] 查找模板基类,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/35373384/

28 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com