C++ Concepts Lite:概念体中的短路

C++ Concepts Lite: Short-circuiting in concept bodies

我正在尝试了解尚未合并到标准中的 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 if Ts = {}。但是,我认为这应该编译,因为 cppereference.com 关于 concepts and constraints 的文档指出

Disjunctions are evaluated left to right and short-circuited (if the left constraint is satisfied, template argument deduction into the right constraint is not attempted).

由于满足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;
}

对这种行为有很好的解释吗? 谢谢。

cppreference上出现的外行解释是正确的。 Select 来自 n4674 draft 的措辞也很清楚:

A conjunction is a constraint taking two operands. A conjunction of constraints is satisfied if and only if both operands are satisfied. The satisfaction of a conjunction’s operands are evaluated left-to-right; if the left operand is not satisfied, template arguments are not substituted into the right operand, and the constraint is not satisfied. […]

(来自 17.10.1.1 逻辑运算 [temp.constr.op] §2。)由于所有精确确定我们如何从概念和模板到原子约束的合取或析取的措辞都相当长,我们将坚持外行的解释。

Is there a good explanation for this behavior? Thanks.

在撰写本文时,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...>;

Coliru demo