MSVC中的Constexpr朋友功能

时间:2019-02-23 15:16:01

标签: c++ c++11 visual-c++ constexpr

我正在尝试在模板中定义constexpr朋友运算符。尝试在非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

此“朋友”区别是标准要求还是编译器错误?

2 个答案:

答案 0 :(得分: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

答案 1 :(得分:0)

[dcl.constexpr]这样说:

  

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

因此,用std::string实例化类模板是完全可以的,尽管比较函数仍然是{em> not 常量表达式,但您的比较函数仍然是constexpr(请通过声明ret constexpr)。标记这些功能constexpr根本不会给您带来任何好处(在此实例中),但这是完全合法的。

然后标准继续说

  

如果在将模板视为非模板函数或构造函数时,如果模板的任何专业化都不能满足constexpr函数或constexpr构造函数的要求,则模板格式错误,无需诊断。

但是,这似乎不适用于您的情况,例如以。内置类型确实满足constexpr的要求。

MSVC的编译错误似乎没有道理。看起来像是编译器中的错误。