gpt4 book ai didi

c++ - 在运算符表达式上下文中纠正重载解析的内置运算符候选的行为

转载 作者:可可西里 更新时间:2023-11-01 17:56:35 24 4
gpt4 key购买 nike

目前,我正在尝试理解C++标准中的[over.match.oper]/7段落,但是遇到以下情况,其中GCC和Clang会产生不同的结果:

https://wandbox.org/permlink/WpoMviA4MHId7iD9

#include <iostream>

void print_type(int) { std::cout << "int" << std::endl; }
void print_type(int*) { std::cout << "int*" << std::endl; }

struct X { X(int*) {} };
struct Y { operator double() { return 0.0; } };

int operator+(X, int) { return 0; } // #1
// T* operator+(T*, std::ptrdiff_t); // #2: a built-in operator (N4659 16.6/14)

int main() {
int* p = 0;
Y y;

print_type(p + y); // This line produces different results for different compilers:
// - gcc HEAD 8.0.0 : always "int" (#1 is called)
// - clang HEAD 6.0.0 : always "int*" (#2 is called)
// - my understanding : "int*" until C++11, ill-formed since C++14

return 0;
}

标准说明

以下是标准版本中相应段落的引用:

C++ 1z(N4659)16.3.1.2 [over.match.oper]第7段
(与 C++ 14(N4140)13.3.1.2 [over.match.oper]第7段基本上相同):

If a built-in candidate is selected by overload resolution, the operands of class type are converted to the types of the corresponding parameters of the selected operation function, except that the second standard conversion sequence of a user-defined conversion sequence (16.3.3.1.2) is not applied. Then the operator is treated as the corresponding built-in operator and interpreted according to Clause 8. [Example:

struct X {
operator double();
};
struct Y {
operator int*();
};
int *a = Y() + 100.0; // error: pointer arithmetic requires integral operand
int *b = Y() + X(); // error: pointer arithmetic requires integral operand

- end example]



C++ 03 13.3.1.2 [over.match.oper]第7段
(与 C++ 11(N3291)13.3.1.2 [over.match.oper]第7段基本上相同):

If a built-in candidate is selected by overload resolution, the operands are converted to the types of the corresponding parameters of the selected operation function. Then the operator is treated as the corresponding built-in operator and interpreted according to clause 5.



CWG 1687引入了C++ 14中的更改。

我天真的解释

最初,我认为顶级代码应该在C++ 14中格式错误。根据这些标准,我对顶级代码的过载解析过程的幼稚理解是(部分代码来自N4659):

首先,生成候选函数集。它包含用户定义的运算符 #1( 16.3.1.2/(3.2))和内置运算符 #2( 16.3.1.2/(3.3)16.6/14)。接下来,为了确定可行函数集,通过为每个自变量/参数对构造隐式转换序列(ICS)来测试两个算子的生存能力。所有ICS均已成功构建为 ICS1(#1) = int* → X( 16.3.3.1.2,用户定义的转换序列), ICS2(#2) = Y → double → int(用户定义的转换序列), ICS1(#2) = int* → int*( 16.3.3.1/6,标识转换,标准转换序列之一)和 ICS2(#2) = X → double → std::ptrdiff_t(用户定义的转换序列) ),因此这两家运营商都是可行的。然后,通过比较ICS来选择最佳可行功能;由于 ICS1(#2)优于 ICS1(#1)( 16.3.3.2/(2.1)),并且 ICS2(#2)不比 ICS2(#1)( 16.3.3.2/3)差,因此 #2#1( 16.3.3/1)更好。最后,通过重载分辨率( 16.3.3/2)选择内置运算符 #2

选择内置运算符时,将应用上面引用的规则( 16.3.1.2/7):将ICS应用于自变量后,运算符表达式的处理将转移至第8条[expr]。此处,ICS的应用在C++ 11和C++ 14中有所不同。在C++ 11中,完全应用了ICS,因此可以考虑使用 (int*) y + (std::ptrdiff_t) (double) n,这很好。而在C++ 14中,未应用用户定义的转换序列中的第二个标准转换序列,因此考虑使用 (int*) y + (double) n。这导致违反语义规则( 8.7/1),即表达式格式错误,并且需要执行才能发出诊断消息。

lang的解释

Clang选择 #2并在8.7/1违规时不带任何诊断消息的情况下调用它。我的猜测是Clang在将调用转移到内置规则(8.7/1)之前完全将ICS应用于参数,这是一个错误。

海湾合作委员会的解释

GCC选择 #1而不进行诊断。 Visual Studio 2017中的Microsoft C/C++编译器的行为似乎相同。另外,这种行为对我来说似乎很合理( 编辑:请参阅[1])。

我的猜测是,GCC认为 #2不可行,然后只有可行的功能才是 #1。但是,我找不到任何规则,例如,如果在用户定义的转换序列中没有第二个标准转换序列,内置运算符的格式就会变得不可行。实际上,当CWG 1687引入短语“用户定义的转换序列的第二标准转换序列除外”时,似乎对生存力的定义没有其他修改。

问题

问题1:根据当前标准,正确的解释是什么?

问题2:如果我的幼稚解释是正确的,那么CWG 1687的行为是否正确?

脚注
  • [1]:不要无声地破坏用C++ 03编写的现有代码,这种行为是不希望的。这可能是CWG 1687决定仅禁用第二个标准转换序列的原因,而保留了生存能力的定义。请参阅下面的评论。

  • 更新

    在此问题之后,针对以下编译器报告了此问题:
  • gcc gcc 81789
  • lang语llvm 34138
  • msc visualstudio 92207
  • 最佳答案

    我同意你的解释。我们有int*Y类型的参数,并且有两个候选者:

    operator+(X, int);                // #1
    operator+(int*, std::ptrdiff_t ); // #2
    #1需要两个用户定义的转换序列, #2需要一个标准转换序列(完全匹配,尽管无关紧要)和一个用户定义的转换序列。对于第一个参数,标准转换序列 is better than是用户定义的转换序列,而对于第二个参数,这两个序列是无法区分的( these conditions均不适用)。由于 #2中的第一个隐式转换序列比 #1中的第一个隐式转换序列更好,并且第二个转换序列是等效的 #2 wins

    然后在CWG 1687之后,我们 don't performdoubleptrdiff_t的最后一次转换,因此结果应该格式错误。

    要回答这个问题:

    is the behavior intended by CWG 1687?



    考虑到示例,我怀疑确实是这样:

    int *b = Y() + X();             // error: pointer arithmetic requires integral operand


    这与您的示例非常相似-唯一的区别是 Y()可转换为 int*而不是直接转换为 int*。我继续提交了 gcc 81789llvm 34138。请注意,clang根本没有实现CWG 1687,该问题的示例和标准编译中均未实现。

    关于c++ - 在运算符表达式上下文中纠正重载解析的内置运算符候选的行为,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/45602368/

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