C ++ Lite Concepts: Short Circuit in Conceptual Bodies

I'm trying to learn about Concepts Lite TS, which has not yet been merged into the standard. I got confused in the behavior of short-circuited disjunctions in conceptual bodies.

Here is a small example:

#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; } 

Compiling with gcc 7.1 and -fconcepts gives an error:

 error: cannot call function 'void myfunc(Ts ...) requires myconcept<int, Ts ...> [with Ts = {}]' 

In this example, std::common_type_t<Ts...> does not exist, since struct std::common_type<Ts...> does not have a type member if Ts = {} . However, I think this should compile because the cppereference.com documentation in terms and conditions states that

Deviations are evaluated from left to right and shorted (if the left constraint is satisfied, then the output of the template argument to the right constraint is not taken).

Since sizeof...(Ts) == 0 is executed, the output of the template argument should not be performed in the second constraint, and the requirement myconcept<int, Ts...> must be satisfied.

It is strange that placing requirements directly in the function declarator leads to compilation of the program:

 #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; } 

Is there a good explanation for this behavior? Thanks.

+5
source share
1 answer

The laymans explanation that appears on cppreference is correct. Choosing a wording from the n4674 draft is also quite clear:

A conjunction is a restriction that takes two operands. A conjunction of constraints is satisfied if and only if both operands are satisfied. Satisfaction of operands of conjunctions is evaluated from left to right; if the left operand is not executed, the template arguments are not substituted into the right operand, and the restriction is not satisfied. [...]

(From 10.17.1.1 Boolean operations [temp.constr.op] ยง2.) Since the entire wording that precisely establishes how we move from concepts and patterns to joining or disjunctioning atomic constraints is quite lengthy, we will stick to laymans' explanation.

Is there a good explanation for this behavior? Thanks.

At the time of this writing, the implementation of the GCC concepts is very experimental. As a workaround, you can reorganize the problematic part into your concept:

 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

+2
source

Source: https://habr.com/ru/post/1269460/


All Articles