Opened 12 years ago
Last modified 10 years ago
#4540 new Feature Requests
fold<Sequence>() instantiates result_of::fold<Sequence const>
Reported by: | anonymous | Owned by: | Douglas Gregor |
---|---|---|---|
Milestone: | Boost 1.44.0 | Component: | result_of |
Version: | Boost Development Trunk | Severity: | Problem |
Keywords: | Cc: | mr.chr.schmidt@… |
Description
Hi,
this is a complicated one, I'll try my best:
there is a problem with fold() and maybe other fusion functions whose result is computed from the result type of user functors.
in iteration/detail/fold.hpp, there are two fold()'s, the default one and a specialization for const sequences:
inline typename result_of::fold<Seq const,State const,F>::type BOOST_FUSION_FOLD_NAME(Seq const& seq,State const& state,F f)
I'm not sure why it is there since there isn't any difference to the non-const fold(). but the fact that it is there causes problems when using fold() with a non-const sequence:
fusion::vector<int> vec; fusion::fold(vec,0,F());
even though the sequence is not const, at least MSVC (and I vaguely remember something about that in the C++ standard) instantiates all result types of the function overload set, before the arguments are matched to the non-const fold().
==> fusion::result_of::fold<Sequence const,State,F> is instantiated.
under normal circumstances this only constitutes an unnecessary instantiation, but when the result type depends on user functors it can cause instantiatiation of undefined types.
for example:
struct F{ template<typename Args> struct result; int operator()(int state,int &element) const{ return state; } }; template<typename State> struct F::result<F(State,int &)>{ typedef int type; }; int main(){ fusion::vector<int> vec; fusion::fold(vec,0,F()); }
the result type of F is only defined for argument type "int &", which is enough for this fold. but the call to fold() instantiates the const specialization of fold(), and therefore instantiates result_of::fold<vector const>, which instantiates the result type of F(State,int const &) ==> compiler error
when the following code snippet is added as a workaround, it works:
template<typename State> struct F::result<F(State,int const &)>{ typedef int type; };
bottom line - the const specialization of fold() probably shouldn't be there.
Change History (7)
comment:1 by , 12 years ago
follow-up: 3 comment:2 by , 12 years ago
I see. but I guess there is another solution, because the following works:
struct A{}; template<typename T> struct fresult; template<> struct fresult<A>{ typedef void type; }; template<typename T> typename fresult<T>::type f(T &){} template<typename T> typename fresult<T const>::type f(T const &){} int main(){ A a; f(a); }
the undefined fresult<T const> is not instantiated for the call to the non-const overload. I don't know what exactly causes the instantiation of result_of::fold<Seq const>, but this suggests there might a solution other than removing the const overload.
comment:3 by , 12 years ago
Resolution: | → wontfix |
---|---|
Status: | new → closed |
Replying to anonymous:
I see. but I guess there is another solution, because the following works:
struct A{}; template<typename T> struct fresult; template<> struct fresult<A>{ typedef void type; }; template<typename T> typename fresult<T>::type f(T &){} template<typename T> typename fresult<T const>::type f(T const &){} int main(){ A a; f(a); }the undefined fresult<T const> is not instantiated for the call to the non-const overload. I don't know what exactly causes the instantiation of result_of::fold<Seq const>, but this suggests there might a solution other than removing the const overload.
That sample compiles fine as SFINAE is taking care of the non-existing specialization. SFINAE does not help in your fusion usecase, though. There is simply no specialization of F::result that matches the template arguments in the first place - and that very instantiation is directly referenced by the boost::result_of implementation. I do not think that this issue can be worked-around.
Here is a minimal non-fusion testcase.
#include <boost/utility/result_of.hpp> struct A{}; struct F { template<typename> struct result; template<typename Self>struct F::result<Self(A&)> {typedef void type;}; #if 0 template<typename Self>struct F::result<Self(A const&)> {typedef void type;}; #endif }; template<typename C> typename boost::result_of<F(C const&)>::type bub(C const&){} template<typename C> typename boost::result_of<F(C&)>::type bub(C&){} int main() { A a; bub(a); }
comment:4 by , 12 years ago
Resolution: | wontfix |
---|---|
Status: | closed → reopened |
reopening this ticket for now, as I think it can be fixed, even though "component" might have to be changed:
BOOST_MPL_HAS_XXX_TRAIT_DEF(type) struct invalid; template<typename F,typename Args> struct result_of_impl{ typedef typename boost::mpl::eval_if< has_type<typename F::template result<Args> >, typename F::template result<Args>, boost::mpl::identity<invalid> >::type type; };
with this fix to result_of your code above yields these results:
A a; bub(a); //ok bub(A()); //error: use of undefined type "invalid"
do you see any problems with this? "invalid" could be further extended to give a meaningful error message, e.g. undefined_result_type<FArgs>
comment:5 by , 12 years ago
Component: | fusion → utility |
---|---|
Type: | Bugs → Feature Requests |
Well, I am not sure about that. Anyway, lets change the component to utility.
BTW. the upcoming c++11 port solves this issue by implementing fold as
template<typename Seq, ...> typename result_of::fold<...>::type fold(Seq&&, ...)
comment:6 by , 12 years ago
Cc: | added |
---|---|
Owner: | changed from | to
Status: | reopened → new |
comment:7 by , 10 years ago
Component: | utility → result_of |
---|
The const overload is necessary to catch non-const rvalues.