MSVC 中的 Constexpr 友元函数
Constexpr friend functions in MSVC
我正在尝试在模板中定义 constexpr 友元运算符。尝试在非 constexpr 上下文中实例化此运算符时出现编译器错误。如果我将完全相同的运算符定义为模板 class 成员作为免费模板函数,它工作正常。
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
这种'friend'歧视是标准的要求还是编译器错误?
我认为,您收到的错误消息可能具有误导性或至少令人困惑。
您的代码的问题是不正确的 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;
}
即WITH 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)
with T
而不是 X
但实际上是一样的,因为内部 T
覆盖外部 T
.
[dcl.constexpr] 是这样说的:
If the instantiated template specialization of a constexpr function template or member function of a class template would fail to satisfy the requirements for a constexpr function or constexpr constructor, that specialization is still a constexpr function or constexpr constructor, even though a call to such a function cannot appear in a constant expression.
所以用 std::string
实例化你的 class 模板是完全可以的,你的比较函数仍然是 constexpr
,尽管对它们的调用是 而不是 常量表达式(通过声明 ret
constexpr
来检查)。标记这些函数 constexpr
根本不会给你买任何东西(在这个实例中),但它是完全合法的。
标准接着说
If no specialization of the template would satisfy the requirements for a constexpr function or constexpr constructor when considered as a non-template function or constructor, the template is ill-formed, no diagnostic required.
这似乎不适用于您的情况,例如实例化内置类型确实满足 constexpr
要求。
MSVC的编译错误好像不合理。它看起来像是编译器中的错误。
我正在尝试在模板中定义 constexpr 友元运算符。尝试在非 constexpr 上下文中实例化此运算符时出现编译器错误。如果我将完全相同的运算符定义为模板 class 成员作为免费模板函数,它工作正常。
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
这种'friend'歧视是标准的要求还是编译器错误?
我认为,您收到的错误消息可能具有误导性或至少令人困惑。
您的代码的问题是不正确的 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;
}
即WITH 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)
with T
而不是 X
但实际上是一样的,因为内部 T
覆盖外部 T
.
[dcl.constexpr] 是这样说的:
If the instantiated template specialization of a constexpr function template or member function of a class template would fail to satisfy the requirements for a constexpr function or constexpr constructor, that specialization is still a constexpr function or constexpr constructor, even though a call to such a function cannot appear in a constant expression.
所以用 std::string
实例化你的 class 模板是完全可以的,你的比较函数仍然是 constexpr
,尽管对它们的调用是 而不是 常量表达式(通过声明 ret
constexpr
来检查)。标记这些函数 constexpr
根本不会给你买任何东西(在这个实例中),但它是完全合法的。
标准接着说
If no specialization of the template would satisfy the requirements for a constexpr function or constexpr constructor when considered as a non-template function or constructor, the template is ill-formed, no diagnostic required.
这似乎不适用于您的情况,例如实例化内置类型确实满足 constexpr
要求。
MSVC的编译错误好像不合理。它看起来像是编译器中的错误。