Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Generic lambdas and const-qualified mem funcs break map and and_then #10

Open
TartanLlama opened this issue Dec 15, 2017 · 2 comments
Open

Comments

@TartanLlama
Copy link

TartanLlama commented Dec 15, 2017

Adding this code to optional_monadic.cpp:

struct foo {
    foo non_const() { return {}; }
};

//...
with_all_optional_implementations<foo>( 
        [](auto make) {
                const auto a = make();
                auto f = [](auto &&x) { return x.non_const(); };
                scelta::map(a, f);
         }
);

results in this compiler error on GCC 7.2:

/home/simon/scelta/test/optional/optional_monadic.cpp: In instantiation of ‘main()::<lambda(auto:16)>::<lambda(auto:17&&)> [with auto:17 = const foo&; auto:16 = test::impl::maker_t<std::optional<foo> >]’:
/home/simon/scelta/include/scelta/./utils/optional_monadic.hpp:30:5:   required by substitution of ‘template<class Optional, class T, class F> constexpr decltype ((scelta::is_nullopt(o) ? forward<decltype (def)>(def) : forward<decltype (f)>(f)(scelta::impl::access_optional(forward<decltype (o)>(o))))) scelta::map_or(Optional&&, T&&, F&&) [with Optional = const std::optional<foo>&; T = std::optional<foo>; F = main()::<lambda(auto:16)> [with auto:16 = test::impl::maker_t<std::optional<foo> >]::<lambda(auto:17&&)>&]’
/home/simon/scelta/include/scelta/./utils/optional_monadic.hpp:39:5:   required by substitution of ‘template<class Optional, class F> constexpr decltype (scelta::map_or(forward<decltype (o)>(o), std::decay_t<_Tp>{}, forward<decltype (f)>(f))) scelta::map(Optional&&, F&&) [with Optional = const std::optional<foo>&; F = main()::<lambda(auto:16)> [with auto:16 = test::impl::maker_t<std::optional<foo> >]::<lambda(auto:17&&)>&]’
/home/simon/scelta/test/optional/optional_monadic.cpp:139:28:   required from ‘main()::<lambda(auto:16)> [with auto:16 = test::impl::maker_t<std::optional<foo> >]’
/home/simon/scelta/test/optional/../variant_test_utils.hpp:117:28:   required from ‘test::with_all_optional_implementations(TF&&)::<lambda(auto:14)> [with auto:14 = test::impl::unpack_alternatives<test::impl::alternatives_t<foo> >::applier<std::optional>; TAlternative = foo; TF = main()::<lambda(auto:16)>]’
/home/simon/scelta/test/optional/../variant_test_utils.hpp:87:10:   required from ‘void test::instantiate_with_all_optional_implementations(TF&&) [with TestCase = test::impl::unpack_alternatives<test::impl::alternatives_t<foo> >::applier; TF = test::with_all_optional_implementations(TF&&) [with TAlternative = foo; TF = main()::<lambda(auto:16)>]::<lambda(auto:14)>]’
/home/simon/scelta/test/optional/../variant_test_utils.hpp:116:80:   required from ‘void test::with_all_optional_implementations(TF&&) [with TAlternative = foo; TF = main()::<lambda(auto:16)>]’
/home/simon/scelta/test/optional/optional_monadic.cpp:141:11:   required from here
/home/simon/scelta/test/optional/optional_monadic.cpp:138:63: error: passing ‘const foo’ as ‘this’ argument discards qualifiers [-fpermissive]
                 auto f = [](auto &&x) { return x.non_const(); };
                                                               ^
/home/simon/scelta/test/optional/optional_monadic.cpp:7:9: note:   in call to ‘foo foo::non_const()’
     foo non_const() { return {}; }
         ^~~~~~~~~
/home/simon/scelta/test/optional/optional_monadic.cpp: In instantiation of ‘main()::<lambda(auto:16)>::<lambda(auto:17&&)> [with auto:17 = const foo&; auto:16 = test::impl::maker_t<boost::optional<foo> >]’:
/home/simon/scelta/include/scelta/./utils/optional_monadic.hpp:30:5:   required by substitution of ‘template<class Optional, class T, class F> constexpr decltype ((scelta::is_nullopt(o) ? forward<decltype (def)>(def) : forward<decltype (f)>(f)(scelta::impl::access_optional(forward<decltype (o)>(o))))) scelta::map_or(Optional&&, T&&, F&&) [with Optional = const boost::optional<foo>&; T = boost::optional<foo>; F = main()::<lambda(auto:16)> [with auto:16 = test::impl::maker_t<boost::optional<foo> >]::<lambda(auto:17&&)>&]’
/home/simon/scelta/include/scelta/./utils/optional_monadic.hpp:39:5:   required by substitution of ‘template<class Optional, class F> constexpr decltype (scelta::map_or(forward<decltype (o)>(o), std::decay_t<_Tp>{}, forward<decltype (f)>(f))) scelta::map(Optional&&, F&&) [with Optional = const boost::optional<foo>&; F = main()::<lambda(auto:16)> [with auto:16 = test::impl::maker_t<boost::optional<foo> >]::<lambda(auto:17&&)>&]’
/home/simon/scelta/test/optional/optional_monadic.cpp:139:28:   required from ‘main()::<lambda(auto:16)> [with auto:16 = test::impl::maker_t<boost::optional<foo> >]’
/home/simon/scelta/test/optional/../variant_test_utils.hpp:117:28:   required from ‘test::with_all_optional_implementations(TF&&)::<lambda(auto:14)> [with auto:14 = test::impl::unpack_alternatives<test::impl::alternatives_t<foo> >::applier<boost::optional>; TAlternative = foo; TF = main()::<lambda(auto:16)>]’
/home/simon/scelta/test/optional/../variant_test_utils.hpp:91:10:   required from ‘void test::instantiate_with_all_optional_implementations(TF&&) [with TestCase = test::impl::unpack_alternatives<test::impl::alternatives_t<foo> >::applier; TF = test::with_all_optional_implementations(TF&&) [with TAlternative = foo; TF = main()::<lambda(auto:16)>]::<lambda(auto:14)>]’
/home/simon/scelta/test/optional/../variant_test_utils.hpp:116:80:   required from ‘void test::with_all_optional_implementations(TF&&) [with TAlternative = foo; TF = main()::<lambda(auto:16)>]’
/home/simon/scelta/test/optional/optional_monadic.cpp:141:11:   required from here
/home/simon/scelta/test/optional/optional_monadic.cpp:138:63: error: passing ‘const foo’ as ‘this’ argument discards qualifiers [-fpermissive]
                 auto f = [](auto &&x) { return x.non_const(); };
                                                               ^
/home/simon/scelta/test/optional/optional_monadic.cpp:7:9: note:   in call to ‘foo foo::non_const()’
     foo non_const() { return {}; }
         ^~~~~~~~~
make[3]: *** [test/CMakeFiles/test.optional.optional_monadic.dir/build.make:63: test/CMakeFiles/test.optional.optional_monadic.dir/optional/optional_monadic.cpp.o] Error 1
make[2]: *** [CMakeFiles/Makefile2:339: test/CMakeFiles/test.optional.optional_monadic.dir/all] Error 2
make[1]: *** [CMakeFiles/Makefile2:75: CMakeFiles/check.dir/rule] Error 2
make: *** [Makefile:175: check] Error 2

Expected behaviour: compile and run

This is because the implementation of map uses trailing return types, even in C++14 mode, which will cause the body of the lambda to be instantiated for the const-qualified overload, giving a hard error (it won't SFINAE). and_then has a similar issue. This could be solved by using auto return type deduction when compiling in C++14 mode.

It took me ages to work out why this broke for my implementation 😆

@vittorioromeo
Copy link
Owner

Thanks for the report, I will look into this. Using automatic return type deduction would prevent SFINAE-friendliness... I wonder if there's a way of solving this without resorting to that.

@TartanLlama
Copy link
Author

TartanLlama commented Dec 15, 2017

I think it's a something of a fundamental issue with making SFINAE friendly wrappers for non-SFINAE friendly callables 😞

You can fob it off on the user and make them give their lambdas return types, but it's not a great solution.

See http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2017/p0826r0.html for some discussion.

I didn't find a better solution then just having separate versions for C++14 and 11, but if you do I'd love to hear it so that I can steal it for my impl 😆

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants