我正在尝试在模板中定义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
此“朋友”区别是标准要求还是编译器错误?
答案 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的编译错误似乎没有道理。看起来像是编译器中的错误。