Constexpr朋友在MSVC中运行

问题描述 投票:3回答:2

我正在尝试在模板中定义constexpr friend运算符。尝试在非constexpr上下文中实例化此运算符时出现编译器错误。如果我将同一个运算符定义为模板类成员作为自由模板函数,它可以正常工作。

template <typename T>
struct A
{
    T Value;

    // error
    friend constexpr bool operator== (const A& l, const A& r)
    {
        return l.Value == r.Value;
    }

    // ok
    constexpr bool operator!= (const A& r) const
    {
        return Value != r.Value;
    }
};

// ok
template <typename T> constexpr bool
operator< (const A<T>& l, const A<T>& r)
{
    return l.Value < r.Value;
}

#include <string>
int main ()
{
    A<std::string> s;
    bool ret = (s < s, s == s, s != s);
}

我得到的错误是

<source>(7): error C3615: constexpr function 'operator ==' cannot result in a constant expression

<source>(9): note: failure was caused by call of undefined function or one not declared 'constexpr'

<source>(9): note: see usage of 'std::operator =='

<source>(8): note: while compiling class template member function 'bool operator ==(const A<std::string> &,const A<std::string> &)'

<source>(29): note: see reference to class template instantiation 'A<std::string>' being compiled

Godbolt link

这种“朋友”歧视是标准或编译器错误的要求吗?

c++ c++11 visual-c++ constexpr
2个回答
0
投票

[dcl.constexpr]这样说:

如果constexpr函数模板的实例化模板特化或类模板的成员函数无法满足constexpr函数或constexpr构造函数的要求,那么该特化仍然是constexpr函数或constexpr构造函数,即使调用这样的函数不能出现在常量表达式中。

所以用std::string实例化你的类模板是完全可以的,你的比较函数仍然是constexpr,虽然对它们的调用不是常量表达式(通过声明ret constexpr来检查这一点)。标记这些功能constexpr根本不会给你任何东西(在这个实例中),但它完全合法。

然后标准继续说

如果模板的特化不满足constexpr函数或constexpr构造函数在被视为非模板函数或构造函数时的要求,则模板格式错误,无需诊断。

然而,这似乎并不适用于您的情况,因为例如内置类型确实满足constexpr要求。

MSVC的编译错误似乎不合理。它看起来像编译器中的一个错误。


0
投票

我认为,你得到的错误信息可能会产生误导或至少令人困惑。你的代码的问题是一个不正确的friend声明。

将模板化结构中的运算符声明为friend使其成为自由函数,就像示例中的operator<一样,因此两个参数而不是像operator!=那样只有一个参数,如您在示例中声明的那样。如果你宣布operator<struct A的朋友,那么这样做的正确方法是:

template <typename  X>
friend constexpr bool operator< (const A<X>& l, const A<X>& r);

operator==也是如此。适当的声明是:

template <typename X>
friend constexpr bool operator== (const A<X>& l, const A<X>& r)
{
    return l.Value == r.Value;
}

即你在有问题的例子中遗漏了与template <typename X>,因此,没有编译。你原来的operator==声明不会为struct A带来一个合适的无朋友函数运算符。

完整的代码清单包括修复程序如下:

template <typename T>
struct A
{
    T Value;

    // no error anymore
    template <typename X>
    friend constexpr bool operator== (const A<X>& l, const A<X>& r)
    {
        return l.Value == r.Value;
    }

    // ok
    constexpr bool operator!= (const A& r) const
    {
        return Value != r.Value;
    }
};

您也可以声明如下

template <typename T>
friend constexpr bool operator== (const A<T>& l, const A<T>& r)

T而不是X,但它实际上是相同的,因为内部T覆盖外部T

© www.soinside.com 2019 - 2024. All rights reserved.