I am trying to learn about the Concepts Lite TS that is yet to be merged into the standard. I'm confused about the behavior of short-circuiting disjunctions in concept 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 the 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 the struct std::common_type<Ts...> does not have a member type if Ts = {}. However, I think this should compile because cppereference.com's documentation on concepts and constraints states that
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).
Since sizeof...(Ts) == 0 is satisfied, template argument deduction should not be attempted on the second constraint and the requirement myconcept<int, Ts...> should be satisfied.
Strangely, placing the requirements directly into the function declarator causes the program to compile:
#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.