简体   繁体   English

编译器中constexpr成员函数的重载解析不一致

[英]Inconsistent overload resolution for constexpr member functions across compilers

I ran into a compiler error that only reproduces on gcc, I narrowed it down to a minimal reproducible sample that also fails on msvc but still compiles fine with clang. 我遇到了一个只在gcc上重现的编译器错误,我把它缩小到一个最小的可重复样本,它也在msvc上失败但仍然可以用clang编译好。 Here's the code: 这是代码:

struct vec
    {
        float _x[2];

        constexpr float operator[](int index) const { return _x[index]; }
        float& operator[](int index) { return _x[index]; }
    };

    struct mat
    {
        vec _x[2];

        constexpr vec operator[](int index) const { return _x[index]; }
        vec& operator[](int index) { return _x[index]; }
    };

    constexpr float bar(float f)
    {
        return f;
    }

    constexpr float one(mat const& m)
    {
        return m[0][0]; // fails in gcc 5+, msvc
    }

    constexpr float two(mat const& m)
    {
        return bar(m[0][0]); // fails in gcc 5+
    }

From what I can tell, overload resolution for vec::operator[] on line 24 does not consider the const overload (line 5) because mat::operator[] const (line 13) returns by value, not by const reference, but I'm not sure why that prevents consideration of vec::operator[] const. 据我所知,第24行的vec :: operator []的重载解析不考虑const重载(第5行),因为mat :: operator [] const(第13行)按值返回,而不是通过const引用返回,但是我不确定为什么会阻止考虑vec :: operator [] const。 Error message from gcc: 来自gcc的错误消息:

: In function 'constexpr float one(const mat&)':
    :24:18: error: call to non-constexpr function 'float& vec::operator[](int)'
        return m[0][0]; // fails in gcc 5+, msvc

And from msvc: 从msvc:

(22): error C3615: constexpr function 'one' cannot result in a constant expression
    (24): note: failure was caused by call of undefined function or one not declared 'constexpr'
    (24): note: see usage of 'vec::operator []'

The original code compiles fine in msvc but the sample does not so it took me a little bit to find what was allowing it work with msvc. 原始代码在msvc中编译得很好,但样本没有,所以我花了一点时间才找到允许它与msvc一起工作的东西。 Apprently, passing the return value through another constexpr function somehow forces msvc to consider the const overload but I have no idea what the reason for this is. 显然,通过另一个constexpr函数传递返回值会以某种方式迫使msvc考虑const重载,但我不知道这是什么原因。 Is this a bug or the result of some esoteric language rules? 这是一个错误还是一些深奥的语言规则的结果? Which compiler is correct? 哪个编译器正确?

Last question here is that this is only a problem because the const overloads return by value, if they return by const reference there are no errors on any compiler. 这里的最后一个问题是,这只是一个问题,因为const重载按值返回,如果它们通过const引用返回,则任何编译器都没有错误。 Is returning by value here a useless pessimization that I should remove? 在这里返回值是一个无用的悲观,我应该删除?

Which compiler is correct? 哪个编译器正确?

In this case, both compilers are correct (or at least, not incorrect). 在这种情况下,两个编译器都是正确的(或者至少是不正确的)。 The C++ Standard says ( [dcl.constexpr]/5 ) that any constexpr function which cannot possibly be invoked in a constant expression makes the program "ill formed; no diagnostic required". C ++标准说( [dcl.constexpr] / 5 )任何constexpr函数都不能在常量表达式中调用,这使得程序“形成错误;无需诊断”。 This means a program with that condition is incorrect, but implementations (compilers) are not required to print any diagnostic message about it. 这意味着具有该条件的程序不正确,但不需要实现(编译器)来打印关于它的任何诊断消息。

You figured correctly that the expression m[0][0] first calls constexpr vec mat::operator[](int) const; 你正确地认为表达式m[0][0]首先调用constexpr vec mat::operator[](int) const; and then calls float& vec::operator[](int); 然后调用float& vec::operator[](int); , and the problem is that this vec::operator[] is not a constexpr function. ,问题是这个vec::operator[]不是constexpr函数。

but I'm not sure why that prevents consideration of vec::operator[] const . 但我不确定为什么会阻止考虑vec::operator[] const

Note that overload resolution for any function call, including an overloadable operator, uses only the types and value categories of the subexpressions, including the implicit class-type argument in the case of a non-static member function. 请注意,任何函数调用(包括可重载运算符)的重载解析仅使用子表达式的类型和值类别,包括非静态成员函数的隐式类型参数。 In particular here, it does not consider whether the expression is being used within a constant expression or within an always-executed piece of a constexpr function. 特别是在这里,它不考虑表达式是在常量表达式中使用还是在constexpr函数的始终执行的片段中使用。 It would just be too confusing if two expressions spelled with the exact same code involved calling entirely different functions depending on how that expression is used. 如果用完全相同的代码拼写的两个表达式涉及根据如何使用该表达式调用完全不同的函数,那将太令人困惑。

In this case, m[0][0] is entirely valid when not used in a constant expression: 在这种情况下, m[0][0]在常量表达式中不使用时完全有效:

float runtime_func(const mat& m) { return m[0][0]; }

and in that use, it calls constexpr vec mat::operator[](int) const; 在那个用途中,它调用constexpr vec mat::operator[](int) const; since the type of m is const-qualified, and then float& vec::operator[](int); 因为m的类型是const限定的,然后是float& vec::operator[](int); since the type of m[0] is vec , not const-qualified. 因为m[0]的类型是vec ,而不是const限定的。 So one and two call exactly the same functions, and both are ill-formed with no diagnostic required. 因此, onetwo调用完全相同的功能,两者都是格式错误,无需诊断。

I can't say why MSVC gives an error for function one but not for function two . 我不能说,为什么MSVC给出了一个错误的功能, one而不是功能two But I don't think it's actually using float vec::operator[](int) const; 但我不认为它实际上是使用float vec::operator[](int) const; instead. 代替。 I notice if I actually try to use two within a constant expression, as in 我注意到我是否真的尝试在常量表达式中使用two ,如

constexpr mat m = {{{{1,2}}, {{3,4}}}};
constexpr float val = two(m);

then MSVC does give somewhat helpful error messages at that point. 然后MSVC确实在那时提供了一些有用的错误消息 That MSVC missed the opportunity to flag the issue earlier can be considered a quality of implementation problem. MSVC错过了提前标记该问题的机会,可被视为实施质量问题。

Is returning by value here a useless pessimization that I should remove? 在这里返回值是一个无用的悲观,我应该删除?

In the code shown, there's no real reason to not just make all four operator[] functions constexpr . 在显示的代码中,没有真正的理由不仅仅使所有四个operator[]函数constexpr Objects that are not const can be used in a constant expression, as long as their initialization happened during that same constant expression, and this could apply to the temporary vec object involved here. const对象可以在常量表达式中使用,只要它们的初始化发生在同一个常量表达式中,并且这可以应用于此处涉及的临时vec对象。 (Of course, there might be some other things to consider in the more complete project where you found an issue before simplifying it for this question.) (当然,在为这个问题简化它之前,你在发现问题的更完整的项目中可能会考虑其他一些事情。)

Last question here is that this is only a problem because the const overloads return by value, if they return by const reference there are no errors on any compiler. 这里的最后一个问题是,这只是一个问题,因为const重载按值返回,如果它们通过const引用返回,则任何编译器都没有错误。 Is returning by value here a useless pessimization that I should remove? 在这里返回值是一个无用的悲观,我应该删除?

This question begins with a false assumption. 这个问题始于错误的假设。 This is not "only a problem because the const overloads return by value". 这不是“唯一的问题,因为const重载按值返回”。 Rather, the problem comes from the const overload of mat::operator[] returning something non-const , which leads to the compiler appropriately applying the non-const overload of vec::operator[] . 相反,问题来自mat::operator[]的const重载,返回非const的东西,这导致编译器适当地应用vec::operator[]的非const重载。

If you were to change the the const overload of mat::operator[] to the following, it would still return by value, but gcc's warning disappears. 如果你要将mat::operator[]const重载更改为以下内容,它仍会按值返回,但gcc的警告会消失。

constexpr const vec operator[](int index) const { return _x[index]; }

This is one of the cases where " constexpr const " is not redundant. 这是“ constexpr const ”不冗余的情况之一。 The " constexpr " qualifies to the function, while the " const " qualifies the returned type. constexpr ”符合函数的要求,而“ const ”则限定返回的类型。

On the other hand, if you are willing to return by reference in the non-const version, why not return by reference in the const version? 另一方面,如果您愿意在非const版本中通过引用返回,为什么不在const版本中通过引用返回? Someone can already get a reference, so what is there to gain by throwing the inefficiency of a copy into the const version? 有人已经可以获得参考,那么通过将副本的低效率抛入const版本可以获得什么? (I might expect to see this the other way around: return by value for non-const so the member is not modified, but return by const reference in the const version for efficiency.) (我可能期望看到另一种方式:非const返回值,因此不修改成员,但在const版本中返回const引用以提高效率。)

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM