I implemented some variadic template functions that work on containers recursively. Consider this basic example:
template<typename T>
concept Iteratable = requires(const T n) {
n.cbegin();
n.cend();
};
template<typename T>
concept Arithmetic = (std::is_arithmetic_v<T>);
template<typename T, typename U>
concept Same = (std::is_same_v<T, U>);
template<typename... Args>
void foo(Args&&... args) {
constexpr auto unpack = [&]<typename T>(T value) {
if constexpr (Iteratable<T>) {
for (auto i : value) {
foo(i);
}
}
else if constexpr (Arithmetic<T> && Same<T, int>) {
std::cout << value << " is an int\n";
}
else if constexpr (Arithmetic<T> && Same<T, double>) {
std::cout << value << " is a double\n";
}
else {
std::cout << value << " is something else\n";
}
};
(unpack(args), ...);
}
int main() {
foo(1, 4, std::vector{ 5.3, 6.7, 7.8 }, std::array{ std::array{ 100 } });
}
this works very well and gives the output:
1 is an int
4 is an int
5.3 is a double
6.7 is a double
7.8 is a double
100 is an int
and now here is bar, which has the exact same logic, except it uses constexpr bool functions instead of Concepts:
template<typename T>
constexpr bool is_iteratable() { return Iteratable<T>; }
template<typename T>
constexpr bool is_arithmetic() { return std::is_arithmetic_v<T>; }
template<typename T, typename U>
constexpr bool is_same() { return std::is_same_v<T, U>; }
template<typename... Args>
void bar(Args&&... args) {
constexpr auto unpack = [&]<typename T>(T value) {
if constexpr (is_iteratable<T>()) {
for (auto i : value) {
bar(i);
}
}
else if constexpr (is_arithmetic<T>() && is_same<T, int>()) {
std::cout << value << " is an int\n";
}
else if constexpr (is_arithmetic<T>() && is_same<T, double>()) {
std::cout << value << " is a double\n";
}
else {
std::cout << value << " is something else\n";
}
};
(unpack(args), ...);
}
int main() {
bar(1, 4, std::vector{ 5.3, 6.7, 7.8 }, std::array{ std::array{ 100 } });
}
clang and gcc compile this, but msvc gives these errors on the line where bar is declared:
C2059 syntax error: ')'
C2143 syntax error: missing ')' before ';'
C2143 syntax error: missing ';' before ')'
(for any on msvc godbolt version I checked it fails, but my work setup is for Visual Studio 2022, 17.0.0 Preview 4.0 /std:c++20).
The weird thing is, I was using these lambdas with if constexpr (bool) for months without any problems, but certain arrangements cause it to suddenly fail with this very ambiguous error. To show this inconsistent behaviour consider foobar which uses a mix of Concepts and constexpr bool checks:
template<typename... Args>
void foobar(Args&&... args) {
constexpr auto unpack = [&]<typename T>(T value) {
if constexpr (Iteratable<T>) {
for (auto i : value) {
foobar(i);
}
}
else if constexpr (is_arithmetic<T>() && is_same<T, int>()) {
std::cout << value << " is an int\n";
}
else if constexpr (is_arithmetic<T>() && is_same<T, double>()) {
std::cout << value << " is a double\n";
}
else {
std::cout << value << " is something else\n";
}
};
(unpack(args), ...);
}
this fails to compile. But if I remove the else if constexpr (is_arithmetic<T>() && is_same<T, double>()) ... statement:
template<typename... Args>
void foobar2(Args&&... args) {
constexpr auto unpack = [&]<typename T>(T value) {
if constexpr (Iteratable<T>) {
for (auto i : value) {
foobar2(i);
}
}
else if constexpr (is_arithmetic<T>() && is_same<T, int>()) {
std::cout << value << " is an int\n";
}
else {
std::cout << value << " is something else\n";
}
};
(unpack(args), ...);
}
it compiles now.
Changing more things, like having an extra if, else if inside an if sometimes causes it to fail again or replacing a constexpr bool check with the corresponding std:: version causes it to work a gain etc. The whole thing is so broken and random. This has to be a bug, right? My workaround is to only use concepts for now...
Try it out:
I reported this bug here.