2013-05-01 18 views
8

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ót Comp::operator() do bool -> 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".

+0

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. –

+0

Aah, masz na myśli 'std :: declval ()' zamiast 'a' w wyrażeniu zwracającym typ zwrotny? Pozwól mi spróbować. Świetny pomysł. – leemes

+0

@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

Odpowiedz

2

Spróbuj zastąpić wyrażenie Fn1 dla a, Fn2 dla b, aby nie wspominać o prywatnych członkach. Próbowałem tego w VC++, ale dostałem inny błąd:

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

Jak już omówiono w komentarzach, naprawiło to błąd kompilacji. Liczba nieskończonych błędów następczych jeszcze nie została usunięta. Musiałem zastosować tę samą poprawkę do 'call', która, jak widać, również używa wstecznego typu powrotu. Zastępując 'f' z' std :: declval () 'naprawiono to. – leemes

+0

Ale powinieneś napisać 'std :: declval ()' zamiast 'T()', ponieważ może on nie mieć domyślnego konstruktora. – leemes

+0

@leemes Right - Właśnie włamałem się w wyrażenie, aby usunąć aib –

6

Clang kompiluje twoje niepowodzenie w teście testowym w porządku i nie widzę żadnego błędu, więc myślę, że to jest błąd GCC. Proszę złożyć raport o błędzie z minimalną repro (nie zawiera), jeśli możesz.

Uwaga: call, tam już coś jak to w standardzie - INVOKE, który nie jest makro, ale koncepcja, że ​​tak powiem. Jest używany przez std::bind, std::function i inne rzeczy, z których jedną jest std::reference_wrapper. Oznacza to, że możesz wykonać std::ref(fun)(args...), aby osiągnąć to samo, co call.

+0

Bardzo dziękuję za podpowiedź na temat "INVOKE". Spróbuję (mogę sprawić, że moje "połączenie" będzie tylko opakowaniem). Powinien sprawić, że mój kod będzie czystszy. I dzięki za próbę Clang. – leemes