我正在尝试了解尚未合并到标准中的Concepts Lite TS。我对概念体中短路分离的行为感到困惑。
这是一个小例子:
#include <type_traits>
#include <iostream>
template <typename T, typename ... Ts> concept bool myconcept =
(sizeof...(Ts) == 0) || (std::is_same_v<T, std::common_type_t<Ts...>>);
template <typename ... Ts>
void myfunc(Ts ... args) requires myconcept<int, Ts...> {
(... , (std::cout << args << std::endl));
}
int main() {
myfunc();
return 0;
}
使用gcc 7.1和-fconcepts进行编译,给出错误:
error: cannot call function 'void myfunc(Ts ...) requires myconcept<int, Ts ...> [with Ts = {}]'
在此示例中,std::common_type_t<Ts...>
不存在,因为结构std::common_type<Ts...>
如果type
没有成员Ts = {}
。但是,我认为这应该编译,因为cppereference.com关于concepts and constraints的文档说明了
从左到右评估析取和短路(如果满足左约束,则不尝试将模板参数推导到右约束中)。
由于满足sizeof...(Ts) == 0
,因此不应对第二个约束尝试模板参数推导,并且应满足要求myconcept<int, Ts...>
。
奇怪的是,将需求直接放入函数声明符会导致程序编译:
#include <type_traits>
#include <iostream>
template <typename ... Ts>
void myfunc(Ts ... args) requires (sizeof...(Ts) == 0) || (std::is_same_v<int, std::common_type_t<Ts...>>) {
(... , (std::cout << args << std::endl));
}
int main() {
myfunc();
return 0;
}
这种行为有一个很好的解释吗? 感谢。
答案 0 :(得分:2)
外行人在cppreference上出现的解释是正确的。从n4674 draft中选择措辞也很清楚:
连词是一个带两个操作数的约束。当且仅当两个操作数都满足时,才满足约束的组合。对连词操作数的满意度从左到右进行评估;如果不满足左操作数,则模板参数不会替换为右操作数,并且不满足约束。 [...]
(来自17.10.1.1逻辑运算[temp.constr.op]§2。)由于所有措辞都精确地确定了我们如何从概念和模板转向原子约束的结合或分离,所以我们将坚持外行的解释。
这种行为有一个很好的解释吗?感谢。
在撰写本文时,GCC的概念实现非常具有实验性。作为一种解决方法,您可以将有问题的部分重构为自己的概念:
template<typename T, typename... Ts>
concept bool refactored = std::is_same_v<T, std::common_type_t<Ts...>>;
template<typename T, typename... Ts>
concept bool myconcept = sizeof...(Ts) == 0 || refactored<T, Ts...>;