C++ 概念 Same 和 Assignable
C++ Concepts Same and Assignable
我最近一直在试验 C++ 概念。我正在尝试以下范围扩展文档中的定义:
http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2016/n4569.pdf
Same
的定义和用法让我很困惑。由于我不知道的原因,作者没有给出明确的定义。所以我正在使用:
template <class T, class U>
concept bool Same()
{
return std::is_same<T, U>::value;
}
问题是文档为 Assignable
给出了以下定义:
template <class T, class U>
concept bool Assignable()
{
return Common<T, U>() && requires(T&& a, U&& b) {
{ std::forward<T>(a) = std::forward<U>(b) } -> Same<T&>;
};
}
它不起作用(在 GCC 6.3 下):一个简单的 Assignable<int&, int&&>()
概念检查给了我 false
(我已经验证 Common
部分没问题)。我必须将 Same<T&>
更改为 T&
才能使其看起来有效。同样的 Same<Type>
检查也用在其他一些地方。
我的问题是:
- 我对
Same
的定义正确吗?
- 为什么使用
Same<T&>
而不是 T&
?有什么不同?
感谢您的帮助。
经过周末的攻关,我想我自己找到了答案。
Eric Niebler 和 Casey Carter 对 Same
有更精确的定义,支持多个模板参数(不仅仅是两个),但我的定义应该基本上适用于双参数情况。
使用-> Type
时,目的是可以将括号中的表达式隐式转换为Type
。使用-> Same<Type>
时,目的是使括号中的表达式正好为Type
。所以他们是不同的。
但是,有一个陷阱。约束检查相当复杂,甚至像 Eric 和 Casey 这样的专家也犯了错误,在 N4569 中给出了错误的定义。 Eric 在 GitHub 上讨论了这个问题:
https://github.com/ericniebler/stl2/issues/330
当按照 N4569 中给出的方式使用时,这意味着表达式应该能够传递给想象中的函数模板,例如
template <typename U>
f(U)
requires Same<T&, U>()
这不起作用——如果传入的表达式是 T
的左值,推导出的 U
是 T
而不是 T&
。解决方案是在 Assignable
中使用 Same<T&>&&
。会产生如下想象的函数模板:
template <typename U>
f(U&&)
requires Same<T&, U>()
现在一切正常——如果传入的表达式是 T
的左值,U
必须推导为 T&
。
玩概念对我来说是一个很好的练习,但我可能应该早点找到他们的代码。他们在以下 GitHub 存储库中有一套完整的概念:
https://github.com/CaseyCarter/cmcstl2
对 C++ 概念感兴趣的人应该研究它。
Same
定义的问题有点微妙:Ranges TS 要求实现将约束 Same<T, U>()
视为等同于 Same<U, T>()
,即识别对称性“T
的类型与 U
相同”:
template <class T, class U>
requires Same<T, U>()
void foo(); // #1
template <class T, class U>
requires Same<U, T>()
void foo(); // redeclaration of #1
template <class T>
requires Same<int, T>()
void bar(); // #2
template <class T>
requires Same<T, int>()
void bar(); // redeclaration of #2
这种等价性无法用语言表达,因为约束规范化规则识别以下各项:
is_same_v<T, U>
is_same_v<U, T>
is_same_v<T, int>
is_same_v<int, T>
作为不同的原子约束。这需要通过编译器内部实现 Same
。
我最近一直在试验 C++ 概念。我正在尝试以下范围扩展文档中的定义:
http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2016/n4569.pdf
Same
的定义和用法让我很困惑。由于我不知道的原因,作者没有给出明确的定义。所以我正在使用:
template <class T, class U>
concept bool Same()
{
return std::is_same<T, U>::value;
}
问题是文档为 Assignable
给出了以下定义:
template <class T, class U>
concept bool Assignable()
{
return Common<T, U>() && requires(T&& a, U&& b) {
{ std::forward<T>(a) = std::forward<U>(b) } -> Same<T&>;
};
}
它不起作用(在 GCC 6.3 下):一个简单的 Assignable<int&, int&&>()
概念检查给了我 false
(我已经验证 Common
部分没问题)。我必须将 Same<T&>
更改为 T&
才能使其看起来有效。同样的 Same<Type>
检查也用在其他一些地方。
我的问题是:
- 我对
Same
的定义正确吗? - 为什么使用
Same<T&>
而不是T&
?有什么不同?
感谢您的帮助。
经过周末的攻关,我想我自己找到了答案。
Eric Niebler 和 Casey Carter 对 Same
有更精确的定义,支持多个模板参数(不仅仅是两个),但我的定义应该基本上适用于双参数情况。
使用-> Type
时,目的是可以将括号中的表达式隐式转换为Type
。使用-> Same<Type>
时,目的是使括号中的表达式正好为Type
。所以他们是不同的。
但是,有一个陷阱。约束检查相当复杂,甚至像 Eric 和 Casey 这样的专家也犯了错误,在 N4569 中给出了错误的定义。 Eric 在 GitHub 上讨论了这个问题:
https://github.com/ericniebler/stl2/issues/330
当按照 N4569 中给出的方式使用时,这意味着表达式应该能够传递给想象中的函数模板,例如
template <typename U>
f(U)
requires Same<T&, U>()
这不起作用——如果传入的表达式是 T
的左值,推导出的 U
是 T
而不是 T&
。解决方案是在 Assignable
中使用 Same<T&>&&
。会产生如下想象的函数模板:
template <typename U>
f(U&&)
requires Same<T&, U>()
现在一切正常——如果传入的表达式是 T
的左值,U
必须推导为 T&
。
玩概念对我来说是一个很好的练习,但我可能应该早点找到他们的代码。他们在以下 GitHub 存储库中有一套完整的概念:
https://github.com/CaseyCarter/cmcstl2
对 C++ 概念感兴趣的人应该研究它。
Same
定义的问题有点微妙:Ranges TS 要求实现将约束 Same<T, U>()
视为等同于 Same<U, T>()
,即识别对称性“T
的类型与 U
相同”:
template <class T, class U>
requires Same<T, U>()
void foo(); // #1
template <class T, class U>
requires Same<U, T>()
void foo(); // redeclaration of #1
template <class T>
requires Same<int, T>()
void bar(); // #2
template <class T>
requires Same<T, int>()
void bar(); // redeclaration of #2
这种等价性无法用语言表达,因为约束规范化规则识别以下各项:
is_same_v<T, U>
is_same_v<U, T>
is_same_v<T, int>
is_same_v<int, T>
作为不同的原子约束。这需要通过编译器内部实现 Same
。