我正在尝试在模板中定义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
这种“朋友”歧视是标准或编译器错误的要求吗?
[dcl.constexpr]这样说:
如果constexpr函数模板的实例化模板特化或类模板的成员函数无法满足constexpr函数或constexpr构造函数的要求,那么该特化仍然是constexpr函数或constexpr构造函数,即使调用这样的函数不能出现在常量表达式中。
所以用std::string
实例化你的类模板是完全可以的,你的比较函数仍然是constexpr
,虽然对它们的调用不是常量表达式(通过声明ret
constexpr
来检查这一点)。标记这些功能constexpr
根本不会给你任何东西(在这个实例中),但它完全合法。
然后标准继续说
如果模板的特化不满足constexpr函数或constexpr构造函数在被视为非模板函数或构造函数时的要求,则模板格式错误,无需诊断。
然而,这似乎并不适用于您的情况,因为例如内置类型确实满足constexpr
要求。
MSVC的编译错误似乎不合理。它看起来像编译器中的一个错误。
我认为,你得到的错误信息可能会产生误导或至少令人困惑。你的代码的问题是一个不正确的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
。