#13012 closed Bugs (fixed)
boost::has_equal_to fails with captureless lambda expression types
Reported by: | Joaquín M López Muñoz | Owned by: | John Maddock |
---|---|---|---|
Milestone: | To Be Determined | Component: | type_traits |
Version: | Boost 1.63.0 | Severity: | Problem |
Keywords: | Cc: |
Description
The following
include <boost/type_traits/has_equal_to.hpp> int main() { auto f=[](){}; (void)boost::has_equal_to<decltype(f)>::value; }
generates a compile time error about an ambiguity for operator==
, see http://coliru.stacked-crooked.com/a/f1010ec991d2721a. The problem has been tested to show in GCC and VS2015. For what it's worth, capturing lambda expressions do not trigger the problem.
Change History (17)
comment:1 by , 5 years ago
Resolution: | → fixed |
---|---|
Status: | new → closed |
comment:2 by , 5 years ago
Hi John, thanks for fixing this.
Unfortunately, in my use case I'm supporting GCC 4.8, which your fix is not. You might want to consider how I'm handling stateless lambdas in non-expression SFINAE scenarios via boost::poly_collection::detail::is_likely_stateless_lambda
at:
Best,
comment:3 by , 5 years ago
I confess I'm not that keen on jumping through hoops for gcc-4.8, however this patch: https://github.com/boostorg/type_traits/commit/28658d6c11228ead4f284f9aaaf0b129a64a3e35 half-fixes the issue - the code will compile, but will return false for all lambdas.
comment:4 by , 5 years ago
Does this work if the signature of the stateless lambda expression is not void()
?
comment:5 by , 5 years ago
No, but I think that signature is mandated by the standard? Or are lambdas with function arguments also convertible to function pointers?
comment:6 by , 5 years ago
I'm afraid stateless lambda expressions of any signature convert to the corresponding function pointer.
comment:7 by , 5 years ago
Nod. I realised that right after I went to bed!
I've pulled your is_likely_stateless_lambda into type traits as a workaround for this case - and it sort of mostly works for gcc-4.7 and 4.8. However, gcc-4.6 and earlier does not handle the code, neither do any of the msvc versions effected (12 and earlier). So it's not much of fix to be honest.
comment:8 by , 5 years ago
Hi John,
This is terrific, thank you. I'm removing my own workaround then and relying on Boost.TypeTraits unconditionally, will report if something arises.
- Do you plan to merge to release in time for 1.67? Just asking to keep Boost.PolyCollection in sync.
- Two nitpicks:
is_likely_lambda.hpp
might be more aptly namedis_likely_stateless_lambda.hpp
.- The copyright notice still points to http://www.boost.org/libs/poly_collection, you might want to change that.
Thanks again
comment:9 by , 5 years ago
Hi,
I see that boost::type_traits_detail::is_likely_stateless_lambda
defaults out for MSVC at
but my local tests seem to show the lambda detection machinery indeed works for MSVC 14.0 --don't know about 12.0. Would it be possible to relax the aforementioned line so that MSVC >=14.0 is accepted?
Thank you
comment:10 by , 5 years ago
MSVC-14 uses the new "accurate" detection code and doesn't need/use the workaround. msvc-12 does need the workaround but can't compile it.
Meanwhile I've just merged to master.
comment:11 by , 5 years ago
Umm, Ive run the following in Visual Studio 2015 against develop
#include <boost/type_traits/has_equal_to.hpp> #include <iostream> int main() { auto f=[](int){return 0;}; std::cout<<boost::has_equal_to<decltype(f),decltype(f),bool>::value<<"\n"; }
and compiler fails with:
1>------ Build started: Project: sandbox, Configuration: Debug Win32 ------ 1> sandbox.cpp 1>d:\usr\joaquin\proyectos\boost-repo\trunk\boost\type_traits\detail\has_binary_operator.hpp(212): error C2593: 'operator ==' is ambiguous 1> d:\usr\joaquin\proyectos\boost-repo\trunk\boost\type_traits\detail\has_binary_operator.hpp(124): note: could be 'boost::detail::has_equal_to_impl::no_operator boost::detail::has_equal_to_impl::operator ==(const boost::detail::has_equal_to_impl::any &,const boost::detail::has_equal_to_impl::any &)' 1> d:\usr\joaquin\proyectos\boost-repo\trunk\boost\type_traits\detail\has_binary_operator.hpp(212): note: while trying to match the argument list '(main::<lambda_7042359f6847725571dfbc3d7e478577>, main::<lambda_7042359f6847725571dfbc3d7e478577>)' 1> d:\usr\joaquin\proyectos\boost-repo\trunk\boost\type_traits\detail\has_binary_operator.hpp(233): note: see reference to class template instantiation 'boost::detail::has_equal_to_impl::operator_exists<Lhs,Rhs>' being compiled 1> with 1> [ 1> Lhs=main::<lambda_7042359f6847725571dfbc3d7e478577>, 1> Rhs=main::<lambda_7042359f6847725571dfbc3d7e478577> 1> ] 1> d:\usr\joaquin\proyectos\boost-repo\trunk\boost\type_traits\detail\has_binary_operator.hpp(262): note: see reference to class template instantiation 'boost::detail::has_equal_to_impl::trait_impl1<main::<lambda_7042359f6847725571dfbc3d7e478577>,main::<lambda_7042359f6847725571dfbc3d7e478577>,Ret,false>' being compiled 1> with 1> [ 1> Ret=bool 1> ] 1> d:\usr\joaquin\proyectos\boost-repo\trunk\boost\type_traits\detail\has_binary_operator.hpp(270): note: see reference to class template instantiation 'boost::detail::has_equal_to_impl::trait_impl<Lhs,Rhs,Ret>' being compiled 1> with 1> [ 1> Lhs=main::<lambda_7042359f6847725571dfbc3d7e478577>, 1> Rhs=main::<lambda_7042359f6847725571dfbc3d7e478577>, 1> Ret=bool 1> ] 1> d:\usr\joaquin\proyectos\poly_collection\sandbox\sandbox.cpp(9): note: see reference to class template instantiation 'boost::has_equal_to<main::<lambda_7042359f6847725571dfbc3d7e478577>,main::<lambda_7042359f6847725571dfbc3d7e478577>,bool>' being compiled 1>d:\usr\joaquin\proyectos\boost-repo\trunk\boost\type_traits\detail\has_binary_operator.hpp(149): error C2593: 'operator ==' is ambiguous 1> d:\usr\joaquin\proyectos\boost-repo\trunk\boost\type_traits\detail\has_binary_operator.hpp(124): note: could be 'boost::detail::has_equal_to_impl::no_operator boost::detail::has_equal_to_impl::operator ==(const boost::detail::has_equal_to_impl::any &,const boost::detail::has_equal_to_impl::any &)' 1> d:\usr\joaquin\proyectos\boost-repo\trunk\boost\type_traits\detail\has_binary_operator.hpp(149): note: while trying to match the argument list '(main::<lambda_7042359f6847725571dfbc3d7e478577>, main::<lambda_7042359f6847725571dfbc3d7e478577>)' 1> d:\usr\joaquin\proyectos\boost-repo\trunk\boost\type_traits\detail\has_binary_operator.hpp(233): note: see reference to class template instantiation 'boost::detail::has_equal_to_impl::operator_returns_void<Lhs,Rhs>' being compiled 1> with 1> [ 1> Lhs=main::<lambda_7042359f6847725571dfbc3d7e478577>, 1> Rhs=main::<lambda_7042359f6847725571dfbc3d7e478577> 1> ] 1>d:\usr\joaquin\proyectos\boost-repo\trunk\boost\type_traits\detail\has_binary_operator.hpp(194): error C2593: 'operator ==' is ambiguous 1> d:\usr\joaquin\proyectos\boost-repo\trunk\boost\type_traits\detail\has_binary_operator.hpp(124): note: could be 'boost::detail::has_equal_to_impl::no_operator boost::detail::has_equal_to_impl::operator ==(const boost::detail::has_equal_to_impl::any &,const boost::detail::has_equal_to_impl::any &)' 1> d:\usr\joaquin\proyectos\boost-repo\trunk\boost\type_traits\detail\has_binary_operator.hpp(194): note: while trying to match the argument list '(main::<lambda_7042359f6847725571dfbc3d7e478577>, main::<lambda_7042359f6847725571dfbc3d7e478577>)' 1> d:\usr\joaquin\proyectos\boost-repo\trunk\boost\type_traits\detail\has_binary_operator.hpp(233): note: see reference to class template instantiation 'boost::detail::has_equal_to_impl::operator_returns_Ret<Lhs,Rhs,Ret,false>' being compiled 1> with 1> [ 1> Lhs=main::<lambda_7042359f6847725571dfbc3d7e478577>, 1> Rhs=main::<lambda_7042359f6847725571dfbc3d7e478577>, 1> Ret=bool 1> ]
comment:12 by , 5 years ago
Works just fine for me, I'm guessing you have an obsolete VC2015 install (not updated), can you add:
#ifdef BOOST_NO_SFINAE_EXPR #pragma message("BOOST_NO_SFINAE_EXPR is set") #endif #ifdef BOOST_NO_CXX11_DECLTYPE #pragma message("BOOST_NO_CXX11_DECLTYPE is set") #endif #ifdef BOOST_TT_HAS_ACCURATE_BINARY_OPERATOR_DETECTION #pragma message("BOOST_TT_HAS_ACCURATE_BINARY_OPERATOR_DETECTION is set") #endif #pragma message(BOOST_STRINGIZE(_MSC_FULL_VER))
To your test case? I get:
BOOST_TT_HAS_ACCURATE_BINARY_OPERATOR_DETECTION is set 190024215
comment:14 by , 5 years ago
OK that's a non-updated VS2015 install. Note that at the current time there is no way to download that version (you automatically get update 3), nor do we have any testers or CI for that version, so the official answer for that issue would always be "please update your visual studio install with the latest patches".
However, if you can run some tests for me it would do no harm to add patches for that version, please just be aware that they will never be tested, so eventual breakage is not entirely unlikely.
Can you cd into libs/type_traits/test and run:
../../../b2 msvc-14.0 define=CI_SUPPRESS_KNOWN_ISSUES define=BOOST_TT_HAS_ACCURATE_BINARY_OPERATOR_DETECTION
I'll assume that will generate errors, if so then inside is_likely_lambda.hpp change:
#elif !defined(BOOST_NO_CXX11_LAMBDAS) && !defined(BOOST_NO_CXX11_DECLTYPE) && !defined(BOOST_NO_CXX11_TEMPLATE_ALIASES) && !defined(BOOST_MSVC)
to
#elif !defined(BOOST_NO_CXX11_LAMBDAS) && !defined(BOOST_NO_CXX11_DECLTYPE) && !defined(BOOST_NO_CXX11_TEMPLATE_ALIASES) && !BOOST_WORKAROUND(BOOST_MSVC, < 1900)
and run the tests again with:
../../../b2 msvc-14.0 define=CI_SUPPRESS_KNOWN_ISSUES
and report back?
Thanks!
comment:15 by , 5 years ago
Reporting back:
- First
b2
run generated a lot of errors as you predicted. Please let me know if you want me to send you a logzip. - Did change the line in
is_likely_lambda.hpp
as instructed. - Second
b2
run failed two tests as follows:
>b2 msvc-14.0 define=CI_SUPPRESS_KNOWN_ISSUES Performing configuration checks - 32-bit : yes (cached) - arm : no (cached) - mips1 : no (cached) - power : no (cached) - sparc : no (cached) - x86 : yes (cached) - symlinks supported : no (cached) - junctions supported : yes (cached) - hardlinks supported : yes (cached) ...patience... ...found 3507 targets... ...updating 8 targets... testing.capture-output ..\..\..\bin.v2\libs\type_traits\test\has_nothrow_destruc tor_test.test\msvc-14.0\debug\threadapi-win32\threading-multi\has_nothrow_destru ctor_test.run ====== BEGIN OUTPUT ====== has_nothrow_destructor_test.cpp:194: <note>The expression: "::boost::has_nothrow _destructor<UDT>::value" did not have the value we wish it to have (found 0, exp ected 1)</note> has_nothrow_destructor_test.cpp:195: <note>The expression: "::boost::has_nothrow _destructor<empty_UDT>::value" did not have the value we wish it to have (found 0, expected 1)</note> has_nothrow_destructor_test.cpp:203: <note>The expression: "::boost::has_nothrow _destructor<trivial_except_destroy>::value" did not have the value we wish it to have (found 0, expected 1)</note> has_nothrow_destructor_test.cpp:207: <note>The expression: "::boost::has_nothrow _destructor<wrap<trivial_except_destroy> >::value" did not have the value we wis h it to have (found 0, expected 1)</note> has_nothrow_destructor_test.cpp:214: The expression: "::boost::has_nothrow_destr uctor<noexcept_destruct>::value" had an invalid value (found 0, expected 1) has_nothrow_destructor_test.cpp:215: The expression: "::boost::has_nothrow_destr uctor<nothrow_destruct>::value" had an invalid value (found 0, expected 1) EXIT STATUS: 2 ====== END OUTPUT ====== set status=0 if %status% NEQ 0 ( echo Skipping test execution due to testing.execute=off exit 0 ) "..\..\..\bin.v2\libs\type_traits\test\has_nothrow_destructor_test.test\msv c-14.0\debug\threadapi-win32\threading-multi\has_nothrow_destructor_test.exe" > "..\..\..\bin.v2\libs\type_traits\test\has_nothrow_destructor_test.test\msvc-1 4.0\debug\threadapi-win32\threading-multi\has_nothrow_destructor_test.output" 2> &1 set status=%ERRORLEVEL% echo. >> "..\..\..\bin.v2\libs\type_traits\test\has_nothrow_destructor_test. test\msvc-14.0\debug\threadapi-win32\threading-multi\has_nothrow_destructor_test .output" echo EXIT STATUS: %status% >> "..\..\..\bin.v2\libs\type_traits\test\has_not hrow_destructor_test.test\msvc-14.0\debug\threadapi-win32\threading-multi\has_no throw_destructor_test.output" if %status% EQU 0 ( copy "..\..\..\bin.v2\libs\type_traits\test\has_nothrow_destructor_test. test\msvc-14.0\debug\threadapi-win32\threading-multi\has_nothrow_destructor_test .output" "..\..\..\bin.v2\libs\type_traits\test\has_nothrow_destructor_test.test \msvc-14.0\debug\threadapi-win32\threading-multi\has_nothrow_destructor_test.run " ) set verbose=0 if %status% NEQ 0 ( set verbose=1 ) if %verbose% EQU 1 ( echo ====== BEGIN OUTPUT ====== type "..\..\..\bin.v2\libs\type_traits\test\has_nothrow_destructor_test. test\msvc-14.0\debug\threadapi-win32\threading-multi\has_nothrow_destructor_test .output" echo ====== END OUTPUT ====== ) exit %status% ...failed testing.capture-output ..\..\..\bin.v2\libs\type_traits\test\has_nothr ow_destructor_test.test\msvc-14.0\debug\threadapi-win32\threading-multi\has_noth row_destructor_test.run... testing.capture-output ..\..\..\bin.v2\libs\type_traits\test\is_default_constr_t est.test\msvc-14.0\debug\threadapi-win32\threading-multi\is_default_constr_test. run ====== BEGIN OUTPUT ====== is_default_constr_test.cpp:200: The expression: "(::boost::is_default_constructi ble<std::pair<deleted_default_construct, int> >::value)" had an invalid value (f ound 1, expected 0) is_default_constr_test.cpp:204: The expression: "(::boost::is_default_constructi ble<std::pair<private_default_construct, int> >::value)" had an invalid value (f ound 1, expected 0) EXIT STATUS: 2 ====== END OUTPUT ====== failed to write output file '..\..\..\bin.v2\libs\type_traits\test\is_nothrow_mo ve_constructible_test_no_intrinsics.test\msvc-14.0\debug\threadapi-win32\threadi ng-multi\is_nothrow_move_constructible_test_no_intrinsics.exe.rsp'! set status=0 if %status% NEQ 0 ( echo Skipping test execution due to testing.execute=off exit 0 ) "..\..\..\bin.v2\libs\type_traits\test\is_default_constr_test.test\msvc-14. 0\debug\threadapi-win32\threading-multi\is_default_constr_test.exe" > "..\..\. .\bin.v2\libs\type_traits\test\is_default_constr_test.test\msvc-14.0\debug\threa dapi-win32\threading-multi\is_default_constr_test.output" 2>&1 set status=%ERRORLEVEL% echo. >> "..\..\..\bin.v2\libs\type_traits\test\is_default_constr_test.test\ msvc-14.0\debug\threadapi-win32\threading-multi\is_default_constr_test.output" echo EXIT STATUS: %status% >> "..\..\..\bin.v2\libs\type_traits\test\is_defa ult_constr_test.test\msvc-14.0\debug\threadapi-win32\threading-multi\is_default_ constr_test.output" if %status% EQU 0 ( copy "..\..\..\bin.v2\libs\type_traits\test\is_default_constr_test.test\ msvc-14.0\debug\threadapi-win32\threading-multi\is_default_constr_test.output" " ..\..\..\bin.v2\libs\type_traits\test\is_default_constr_test.test\msvc-14.0\debu g\threadapi-win32\threading-multi\is_default_constr_test.run" ) set verbose=0 if %status% NEQ 0 ( set verbose=1 ) if %verbose% EQU 1 ( echo ====== BEGIN OUTPUT ====== type "..\..\..\bin.v2\libs\type_traits\test\is_default_constr_test.test\ msvc-14.0\debug\threadapi-win32\threading-multi\is_default_constr_test.output" echo ====== END OUTPUT ====== ) exit %status% ...failed testing.capture-output ..\..\..\bin.v2\libs\type_traits\test\is_defaul t_constr_test.test\msvc-14.0\debug\threadapi-win32\threading-multi\is_default_co nstr_test.run...
comment:16 by , 5 years ago
Thanks for that, pushed to develop, once CI has completed I'll merge to master.
comment:17 by , 5 years ago
Thank you, happily using your improved has_equal_to
, waiting for test runners to cycle before merging to master (hopefully in time for Boost 1.67).
This is a somewhat documented issue, the example in the docs is:
A similar situation occurs with a captureless lambda, because
f
in your example above is really a class that looks like:However, it turns out that in C++11 we can do better, for modern compilers this is fixed in the sequence of commits in: https://github.com/boostorg/type_traits/pull/64
Note that I still have to update the docs.
Also note that whether a lambda is comparable to other lambdas is compiler specific: it is for gcc (via unambiguous conversion to function pointer), but not for msvc which has implicit conversions to 2 different function pointer types and is therefore ambiguous. So the new code reports
true
for gcc andfalse
for msvc.