https://godbolt.org/z/EoeGs3E3v
#ifdef WITH_CONCEPT
# include <concepts>
# define CONCEPT std::same_as<unsigned>
#else
# define CONCEPT
#endif
template<unsigned...Ns>
//requires(sizeof...(Ns) > 0) // workaround
struct Foo {
static_assert(sizeof...(Ns) > 0);
static constexpr auto cnt = sizeof...(Ns);
static constexpr auto cnt0 = (0u + ... + (Ns == 0));
constexpr Foo() requires(cnt0 == 0) {}
constexpr explicit(cnt0 == 1)
Foo(CONCEPT auto...args)
requires(cnt0 > 0 && cnt0 == sizeof...(args))
{}
};
template<Foo> void func() {}
int main() {
// This is okey, but would have same problem if `auto` is replaced with `Foo`
constexpr auto F = Foo<1>{};
// Some compilers instantiate Foo<> which has hard error
func<F>();
}
No WITH_CONCEPT WITH_CONCEPT
Clang ❎ ❎
GCC ❎ ✅
MSVC ✅ ✅
What does the standard say about this?
Edit: Partially reduced to Does NTTP construction always happen for CTAD even if value of type of that specialization is given?
Clang output:
<source>:11:19: error: static assertion failed due to requirement 'sizeof...(Ns) > 0'
11 | static_assert(sizeof...(Ns) > 0);
| ^~~~~~~~~~~~~~~~~
<source>:18:24: note: in instantiation of template class 'Foo<>' requested here
18 | constexpr explicit(cnt0 == 1)
| ^
<source>:31:10: note: while substituting deduced template arguments into function template '<deduction guide for Foo>' [with Ns = (no value), args:auto = <Foo<1>>]
31 | func<F>();
| ^
<source>:31:5: note: while substituting explicitly-specified template arguments into function template 'func'
31 | func<F>();
| ^
<source>:11:33: note: expression evaluates to '0 > 0'
11 | static_assert(sizeof...(Ns) > 0);
| ~~~~~~~~~~~~~~^~~
1 error generated.
3