"Member is private" although I don't access it from outside, when using trailing return type

c++, c++11, functional-programming, metaprogramming, templates

Solution

Try substituting an expression of Fn1 for a, Fn2 for b to avoid mentioning private members. I tried this in VC++, but got a different error:

template<class Arg> inline
auto operator()(const Arg & arg) const -> decltype(call(Fn1(), call(Fn2(), arg))) {
    return call(b, call(a, arg));
}

Problem

How can I fix the following problem? I'm writing some functional library which defines the following functions which are relevant for this question: - `call(f,arg)`: Calls a function with an argument. Just a wrapper I need for some situations. - `comp(f1,f2)`: Returns a composition of two functions. Returns a helper functor representing the composition of the two functions. The implementation looks like the following (simplified versions which still demonstrate the problem): ``` // Call f with one argument template <class Fn, class Arg> auto call(const Fn &f, const Arg & arg) -> decltype(f(arg)) { return f(arg); } // Helper functor for the function below template<class Fn1, class Fn2> class CompFn { Fn1 a; Fn2 b; public: CompFn(const Fn1 &f1, const Fn2 &f2) : a(f1), b(f2) {} template<class Arg> inline auto operator()(const Arg & arg) const -> decltype(call(b, call(a, arg))) { return call(b, call(a, arg)); } }; /** Composition of f1 and f2 (f2 after f1). */ template<class Fn1, class Fn2> CompFn<Fn1,Fn2> comp(const Fn1 &f1, const Fn2 &f2) { return CompFn<Fn1,Fn2>(f1, f2); } ``` The following code is used as a simple test: ``` // Example: Take the length of the string and compare it against zero. std::function<int(std::string)> stringLength = [](std::string s) { return s.size(); }; std::function<bool(int)> greaterZero = [](int x) { return x > 0; }; auto stringNotEmpty = comp(stringLength, greaterZero); std::string testInput1 = "foo"; std::string testInput2 = ""; ``` Until here, everything works fine. Calling `comp` itself doesn't seem to be a problem. Calling the resulting function directly is also OK. But calling the composition via `call` introduces an infinity number of compilation errors (yaaay, new record!): ``` assert(call(stringNotEmpty,testInput1) == true); // line 44 assert(call(stringNotEmpty,testInput2) == false); ``` The compilation output (gcc 4.7, full output see ideone links below): ``` prog.cpp:16:9: error: ‘std::function<bool(int)> CompFn<std::function<int(std::basic_string<char>)>, std::function<bool(int)> >::b’ is private prog.cpp:44:5: error: within this context prog.cpp:15:9: error: ‘std::function<int(std::basic_string<char>)> CompFn<std::function<int(std::basic_string<char>)>, std::function<bool(int)> >::a’ is private prog.cpp:44:5: error: within this context prog.cpp:22:10: error: template instantiation depth exceeds maximum of 900 (use -ftemplate-depth= to increase the maximum) substituting ‘template<class Fn, class Arg> decltype (f(arg)) call(const Fn&, const Arg&) [with Fn = std::function<int(std::basic_string<char>)>; Arg = std::basic_string<char>]’ prog.cpp:22:10: required by substitution of ‘template<class Arg> decltype (call(((const CompFn*)this)->CompFn<Fn1, Fn2>::b, call(((const CompFn*)this)->CompFn<Fn1, Fn2>::a, arg))) CompFn::operator()(const Arg&) const [with Arg = Arg; Fn1 = std::function<int(std::basic_string<char>)>; Fn2 = std::function<bool(int)>] [with Arg = std::basic_string<char>]’ prog.cpp:8:6: required by substitution of ‘template<class Fn, class Arg> decltype (f(arg)) call(const Fn&, const Arg&) [with Fn = std::function<int(std::basic_string<char>)>; Arg = std::basic_string<char>]’ prog.cpp:22:10: required by substitution of ‘template<class Arg> decltype (call(((const CompFn*)this)->CompFn<Fn1, Fn2>::b, call(((const CompFn*)this)->CompFn<Fn1, Fn2>::a, arg))) CompFn::operator()(const Arg&) const [with Arg = Arg; Fn1 = std::function<int(std::basic_string<char>)>; Fn2 = std::function<bool(int)>] [with Arg = std::basic_string<char>]’ prog.cpp:8:6: required by substitution of ‘template<class Fn, class Arg> decltype (f(arg)) call(const Fn&, const Arg&) [with Fn = std::function<int(std::basic_string<char>)>; Arg = std::basic_string<char>]’ prog.cpp:22:10: required by substitution of ‘template<class Arg> decltype (call(((const CompFn*)this)->CompFn<Fn1, Fn2>::b, call(((const CompFn*)this)->CompFn<Fn1, Fn2>::a, arg))) CompFn::operator()(const Arg&) const [with Arg = Arg; Fn1 = std::function<int(std::basic_string<char>)>; Fn2 = std::function<bool(int)>] [with Arg = std::basic_string<char>]’ prog.cpp:8:6: [ skipping 890 instantiation contexts ] [ ...continues endlessly... ] ``` When converting the composition to a `std::function`, it's also perfectly fine. But this will not allow to use polymorphic functors with my `comp` function, at least I don't see an option. One "fix" is to not use trailing return type with decltype for the `Comp::operator()`, but fixing the return type to `bool` (specialized for this single test scenario). All four mentioned test cases summarized: - Test1 -- Call the composition directly --> OK - Test2 -- Call the composition using `call` --> Error - Test3 -- Cast the composition to std::function, then call using `call` --> OK - Test4 -- Call the composition using `call`. Fixed return type of `Comp::operator()` to `bool` --> OK My goal is to make `call` a "seemless" wrapper to call any type of function: Functors, function pointers, member function pointers, member variable pointers, etc..., and also a composition using `comp`. I have a bunch of overloads for them but I don't want to introduce an overload for `Comp<Fn1,Fn2>` since `Fn1` or `Fn2` can again be any type of function, it seems to be a "recursive problem".

Original source