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 Steven Watanabe, 12 years ago

The const overload is necessary to catch non-const rvalues.

comment:2 by anonymous, 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.

in reply to:  2 comment:3 by Christopher Schmidt, 12 years ago

Resolution: wontfix
Status: newclosed

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 anonymous, 12 years ago

Resolution: wontfix
Status: closedreopened

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 Christopher Schmidt, 12 years ago

Component: fusionutility
Type: BugsFeature 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 Christopher Schmidt, 12 years ago

Cc: mr.chr.schmidt@… added
Owner: changed from Joel de Guzman to Douglas Gregor
Status: reopenednew

comment:7 by viboes, 10 years ago

Component: utilityresult_of
Note: See TracTickets for help on using tickets.