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

Viewed 320

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.

1 Answers
Related