Jak mogę naprawić następujący problem?"Członek jest prywatny", chociaż nie mam do niego dostępu z zewnątrz, gdy używany jest końcowy typ zwracania
Piszę trochę funkcjonalną bibliotekę, która określa następujące funkcje, które są istotne dla tego pytania:
call(f,arg)
: Wywołuje funkcję z argumentem. Tylko opakowanie potrzebne w niektórych sytuacjach.comp(f1,f2)
: Zwraca skład dwóch funkcji. Zwraca pomocniczy element reprezentujący skład dwóch funkcji.
Realizacja wygląda następująco (uproszczone wersje, które nadal wykazują problemu):
// 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);
}
Poniższy kod jest używany jako prosty 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 = "";
Do dnia Tutaj wszystko działa w porządku. Samo wywołanie nie wydaje się być problemem. Wywołanie bezpośrednio wynikowej funkcji jest również w porządku. Ale powołanie składu poprzez call
wprowadza szereg nieskończoność błędów kompilacji (yaaay, nowy rekord!):
assert(call(stringNotEmpty,testInput1) == true); // line 44
assert(call(stringNotEmpty,testInput2) == false);
Wyjście kompilacji (GCC 4.7, pełna moc widzieć linki ideone poniżej):
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... ]
Podczas konwertowania kompozycji na std::function
, jest to również doskonałe. Ale to nie pozwoli na użycie funktorów polimorficznych z moją funkcją comp
, przynajmniej nie widzę opcji.
One „fix” jest nie używać spływu typ zwracany z decltype dla Comp::operator()
, ale ustalające typ zwracany do bool
(specjalistyczne dla tego jednego scenariusza testu).
Wszystkie cztery wymienione przypadki testowe podsumować:
- Test1 - Call składu bezpośrednio -> OK
- Test2 - Call składu korzystając
call
-> Błąd - Test3 - - Prześlij kompozycję na std :: function, a następnie zadzwoń, używając
call
-> OK - Test4 - Zadzwoń do komputera osition za pomocą
call
. Poprawiono typ powrótComp::operator()
dobool
-> OK
Moim celem jest, abycall
A „seemless” wrapper zadzwonić każdy rodzaj funkcji: funktory, wskaźniki funkcyjne, wskaźników funkcji członek, członek zmienne wskaźniki, itd ..., a także kompozycja korzystająca z comp
. Mam kilka przeciążeń dla nich, ale nie chcę wprowadzić przeciążenia dla Comp<Fn1,Fn2>
od Fn1
lub Fn2
może znowu być dowolnym typem funkcji, wydaje się być "problemem rekursywnym".
Co jeśli podstawisz jakieś wyrażenie Fn1 dla a, Fn2 dla b? Nie jest całkowicie hermetyzowany, ale nadal używa zwrotnego typu powrotu, nie wspominając o członkach prywatnych. –
Aah, masz na myśli 'std :: declval()' zamiast 'a' w wyrażeniu zwracającym typ zwrotny? Pozwól mi spróbować. Świetny pomysł. –
leemes
@ScottJones Jesteś moim bohaterem dnia. Proszę zaksięguj to jako odpowiedź, a ja to zaakceptuję. Bardzo dobry połów! Jednak wydaje się, że jest to błąd kompilatora, ponieważ decltype powinno jedynie "obliczyć" typ wyrażenia bez wzięcia pod uwagę niektórych specyfikatorów dostępu (chyba). Clang wydaje się ją kompilować, zgodnie z odpowiedzią Xeo. – leemes