Open
Description
The test code below does not compile (in C++20 mode) with any version of clang I could test in Compiler Explorer (up to and including clang 15):
#include <type_traits>
// Generic type holder, for types that cannot be instanciated.
template<typename T>
struct type_holder {};
// Generic type to create a function with a given signature.
template<typename T>
struct foo;
template<typename R, typename ... Args>
struct foo<R(Args...)> {
static R bar(Args...) {}
};
// Offending code.
template<typename T, typename ... IArgs>
auto test(IArgs... inputs) {
[&]<typename R, typename... Args>(type_holder<R(Args...)>, Args... values) {
// This always works.
foo<T>::bar(values...);
// This does not always work.
if constexpr (std::is_same_v<R, void>) {
if constexpr (sizeof...(Args) > 0) {
foo<T>::bar(values...);
} else {
foo<T>::bar();
}
} else {
int return_value = 0;
if constexpr (sizeof...(Args) > 0) {
return_value = foo<T>::bar(values...);
} else {
return_value = foo<T>::bar();
}
}
}(type_holder<T>{}, inputs...);
}
int main() {
// <source>:35:32: error: assigning to 'int' from incompatible type 'void'
// return_value = foo<T>::bar();
// ^~~~~~~~~~~~~
test<void()>();
//<source>:28:29: error: too few arguments to function call, expected 1, have 0
// foo<T>::bar();
// ~~~~~~~~~~~ ^
test<void(int)>(1);
// works!
test<int()>();
//<source>:28:29: error: too few arguments to function call, expected 1, have 0
// foo<T>::bar();
// ~~~~~~~~~~~ ^
test<int(int)>(1);
return 0;
}
Test cases:
- As you can see from the errors reported,
test<void()>()
generates an error inside anif constexpr
branch that should not be entered.std::is_same_v<R, void>
should be true, but it took (or at least, also tried to compile) the false branch. - Similar story with
test<void(int)>(1)
. There it took the correct branch for the return type, but then took the wrong branch for the arguments.sizeof...(Args) > 0
should be true, but it took (or at least, also tried to compile) the false branch. test<int()>()
works fine, for some reason (it is the case that corresponds to theelse
branch for allif constexpr
checks).test<int(int)>(1)
fails again and takes the wrong branch for the return value and the arguments.
Extra information:
- The code compiles if using
foo<R(Args...)>
instead offoo<T>
, which should be equivalent. Weirdly enough, as this seems orthogonal to theif constexpr
issue. - The code always failed to compile with clang since generic lambda with explicit template parameters were added (clang 9 with
-std=c++2a
). - The code compiles with GCC 8 (
-std=c++2a
) and above. - The code compiles with MSVC 19.30 and above.