The following compiles with GCC, but not with Clang:
#include <array>
#include <tuple>
constexpr auto makeNumberGenerator = [](this auto maker, int startingValue)
{
return [maker, startingValue]<typename Generator>(this Generator generator) -> std::tuple<int, Generator>
{
return std::make_tuple(startingValue, maker(startingValue + 1));
};
};
auto test = []{
std::array<int, 5> arr;
auto fn = [](this auto self, int* output, const int* end, auto callable) -> void
{
auto [value, newCallable] = callable();
if (output != end)
{
*output = value;
self(output + 1, end, newCallable);
}
};
fn(std::begin(arr), std::end(arr), makeNumberGenerator(0));
return arr;
}();
On compiler explorer: GCC, Clang. Flags used: -O2 -std=c++23.
Outputs:
GCC:
test:
.long 0
.long 1
.long 2
.long 3
.long 4
Clang:
<source>:9:47: error: function 'operator()<(lambda at <source>:5:38)>' with deduced return type cannot be used before it is defined
9 | return std::make_tuple(startingValue, maker(startingValue + 1));
| ^
<source>:8:5: note: while substituting into a lambda expression here
8 | {
| ^
<source>:26:59: note: in instantiation of function template specialization '(anonymous class)::operator()<(lambda at <source>:5:38)>' requested here
26 | fn(std::begin(arr), std::end(arr), makeNumberGenerator(0));
| ^
<source>:5:38: note: 'operator()<(lambda at <source>:5:38)>' declared here
5 | constexpr auto makeNumberGenerator = [](this auto maker, int startingValue)
| ^
1 error generated.
Compiler returned: 1
Which compiler is correct? Should this code compile according to the C++23 standard?
EDIT: This works on both:
constexpr auto makeNumberGenerator = [](this auto self, int startingValue)
{
return std::bind([startingValue](auto maker)
{
return std::make_tuple(startingValue, maker(startingValue + 1));
}, self);
};
Changing std::bind to a simple lambda gives the same error as before.
EDIT2: Apparently, this problem is unrelated to deducing this, as this code presents the exact same issue (compiles on GCC, not on Clang):
#include <array>
#include <tuple>
constexpr auto makeNumberGenerator = [](auto maker, int startingValue)
{
return [maker, startingValue]<typename Generator>(Generator generator) -> std::tuple<int, Generator>
{
return std::make_tuple(startingValue, maker(maker, startingValue + 1));
};
};
auto test = []{
std::array<int, 5> arr;
auto fn = [](auto self, int* output, const int* end, auto callable) -> void
{
auto [value, newCallable] = callable(callable);
if (output != end)
{
*output = value;
self(self, output + 1, end, newCallable);
}
};
fn(fn, std::begin(arr), std::end(arr), makeNumberGenerator(makeNumberGenerator, 0));
return arr;
}();