26

To pytanie jest inspirowana w following solution do wielokrotnego dziedziczenia przeciążenia pseudo-dwuznaczności, co jest miłym sposobem wdrożenia odwiedzających lambda dla boost :: wariant zaproponowany w this answer:użyciu deklaracja zmiennej liczbie argumentów szablonu

chcę zrobić coś podobnego do następującego:

template <typename ReturnType, typename... Lambdas> 
struct lambda_visitor : public boost::static_visitor<ReturnType>, public Lambdas... { 
    using Lambdas...::operator(); //<--- doesn't seem to work 
    lambda_visitor(Lambdas... lambdas) : boost::static_visitor<ReturnType>() , Lambdas(lambdas)... { } 
}; 

Nie jestem pewien, jaka byłaby właściwa składnia dodawania klauzul wykorzystujących do list typu spakowanego. Klauzula using ma kluczowe znaczenie, aby powstrzymać kompilator od narzekania, że ​​operator() są niejednoznaczne, co w zupełności nie jest, ponieważ mają one wszystkie różne sygnatury.

+3

istnieje "ekspansje zapakować using-deklaracji" [P0195R2] (http://wg21.link/p0195r2) wniosku. – Orient

+1

Rozszerzenie pakietu parametrów w kontekście deklaracji "using" znajduje się w standardzie C++ 17. – ThomasMcLeod

Odpowiedz

31

Ok okazało się całkiem przyzwoity rozwiązanie:

zasadzie muszę rozpakować jeden dodatkowy przypadek lambda i zastosowania klauzuli using do niespakowanego lambda i resztą, ale w tym przypadku, bo widocznie nie mogę zrobić o zmiennej liczbie argumentów lista używając deklaracji (przynajmniej ja nie znam składni, jeśli jego możliwości), reszta jest owinięty przez dziedziczenie z „odpoczynek” przypadku tak:

template <typename ReturnType, typename... Lambdas> 
struct lambda_visitor; 

template <typename ReturnType, typename Lambda1, typename... Lambdas> 
struct lambda_visitor< ReturnType, Lambda1 , Lambdas...> 
    : public lambda_visitor<ReturnType, Lambdas...>, public Lambda1 { 

    using Lambda1::operator(); 
    using lambda_visitor< ReturnType , Lambdas...>::operator(); 
    lambda_visitor(Lambda1 l1, Lambdas... lambdas) 
     : Lambda1(l1), lambda_visitor< ReturnType , Lambdas...> (lambdas...) 
    {} 
}; 


template <typename ReturnType, typename Lambda1> 
struct lambda_visitor<ReturnType, Lambda1> 
    : public boost::static_visitor<ReturnType>, public Lambda1 { 

    using Lambda1::operator(); 
    lambda_visitor(Lambda1 l1) 
     : boost::static_visitor<ReturnType>(), Lambda1(l1) 
    {} 
}; 


template <typename ReturnType> 
struct lambda_visitor<ReturnType> 
    : public boost::static_visitor<ReturnType> { 

    lambda_visitor() : boost::static_visitor<ReturnType>() {} 
}; 

więc mogę to zrobić indukcyjnie poprzez umieszczenie dwóch przy użyciu deklaracji, jednej z rozpakowanego typu lambda i drugiej z klasy macierzystej, która jest ta sama klasa z mniejszą ilością lambda.

+0

Ah, widziałem twoje komentarze w mojej odpowiedzi i miałem zamiar opublikować to rozwiązanie teraz :) Szkoda, że ​​eleganckie rozwiązanie staje się trochę owłosione :( –

+0

@RMartinho, niezupełnie, wciąż jest całkiem fajnym rozwiązaniem, dzięki – lurscher

+0

Dzięki Nauczyłem się kolejnego dziwnego przypadku w rogu C++! :) –

5

To jest stare pytanie i wspaniała odpowiedź. Jest jeszcze jedna rzecz, którą możemy zrobić, aby poprawić IMHO.

W języku C++ 14 i lepszym nie musimy określać typu zwrotu - można go wywnioskować.

#include <boost/variant.hpp> 
#include <type_traits> 

namespace detail { 

    template<typename... Lambdas> 
    struct lambda_visitor; 

    template<typename Lambda1, typename... Lambdas> 
    struct lambda_visitor<Lambda1, Lambdas...> 
     : public lambda_visitor<Lambdas...>, 
      public Lambda1 
    { 

     using Lambda1::operator(); 
     using lambda_visitor<Lambdas...>::operator(); 

     lambda_visitor(Lambda1 l1, Lambdas... lambdas) 
      : Lambda1(l1) 
      , lambda_visitor<Lambdas...>(lambdas...) {} 
    }; 

    template<typename Lambda1> 
    struct lambda_visitor<Lambda1> 
     : 
      public Lambda1 
    { 

     using Lambda1::operator(); 

     lambda_visitor(Lambda1 l1) 
      : Lambda1(l1) {} 
    }; 
} 

template<class...Fs> 
auto compose(Fs&& ...fs) 
{ 
    using visitor_type = detail::lambda_visitor<std::decay_t<Fs>...>; 
    return visitor_type(std::forward<Fs>(fs)...); 
}; 

przypadek użycia:

boost::variant<int, std::string> x = "foo", y = 4; 

auto visitor = compose([](const int& i) 
         { 
          std::cout << i << std::endl; 
         }, 
         [](const std::string& s) 
         { 
          std::cout << s << std::endl; 
         }); 

boost::apply_visitor(visitor, x); 
boost::apply_visitor(visitor, y); 
Powiązane problemy