15

Eksperymentuję z nowymi funkcjami C++ 11. W mojej konfiguracji bardzo chciałbym używać dziedziczących konstruktorów, ale niestety żaden kompilator ich jeszcze nie implementuje. Dlatego próbuję zasymulować to samo zachowanie. Mogę napisać coś takiego:wykrywanie chronionych konstruktorów (prawdopodobnie abstrakcyjnych) klasy bazowej

template <class T> 
class Wrapper : public T { 
    public: 
    template <typename... As> 
    Wrapper(As && ... as) : T { std::forward<As>(as)... } { } 
    // ... nice additions to T ... 
}; 

To działa ... przez większość czasu. Czasami kod używający klasy Wrapper musi wykorzystywać SFINAE, aby wykryć, jak można skonstruować taki model Wrapper<T>. Jest jednak następujący problem: jeśli chodzi o rozdzielczość przeciążania, konstruktor Wrapper<T> zaakceptuje dowolne argumenty - ale wtedy kompilacja nie powiedzie się (i jest to nie objęte SFINAE), jeśli typ T nie może zostać skonstruowany z wykorzystaniem tych.

starałem się warunkowo włączyć różne dawałaby szablonu konstruktora używając enable_if

template <typename... As, typename std::enable_if<std::is_constructible<T, As && ...>::value, int>::type = 0> 
    Wrapper(As && ... as) // ... 

który działa dobrze tak długo, jak:

  • odpowiedni konstruktor T jest public
  • T nie jest abstrakcyjna

Moje pytanie brzmi: jak pozbyć się powyższych dwóch ograniczeń?

że próbuje przezwyciężyć najpierw sprawdzenie (stosując SFINAE i sizeof()) czy ekspresja new T(std::declval<As &&>()...) dobrze uformowanej ciąguWrapper<T>. Ale to oczywiście nie działa, ponieważ jedynym sposobem, w jaki klasa pochodna może korzystać z chronionego konstruktora bazy, jest lista inicjalizacji członka.

Po drugie, nie mam pojęcia - i jest to ten, którego potrzebuję więcej, ponieważ czasami jest to Wrapper, który implementuje abstrakcyjne funkcje T, co czyni go kompletnym typem.

że chce roztwór którym:...

  • jest prawidłowy standardowych
  • prace lub gcc-4,6 * gcc-4,7 * lub szczęk-3 *

Dzięki!

+0

Jestem w pośpiechu, ale może http://stackoverflow.com/questions/8984013/can-sfinae-detect-private-access-violations może pomóc tutaj, nie liczyłbym na gcc 4.6 dostając to prawy chociaż – PlasmaHH

+1

Kontrola dostępu jest tu nieco trudna: jeśli użyjesz 'sizeof()', kompilator sprawdzi całe wyrażenie, dostęp zostanie włączony - ale potem zostanie sprawdzony dostęp ** z kontekstu wyrażenia **, co nie powiedzie się w przypadku chronionych konstruktorów; wszystko inne niż 'sizeof' działa tylko na poziomie rozdzielczości przeciążenia i typu wnioskowania, więc naruszenia dostępu nie wywołają SFINAE - ale wtedy nie widzę możliwości zrobienia czegoś z konstruktorem, ponieważ nie można go przekazać jako argumentu szablonu . Jeśli chodzi o obsługę kompilatora, będę szczęśliwy, jeśli ** dowolny ** z powyższych akceptuje kod. –

Odpowiedz

12

Wygląda na to, że działa dobrze na moim lokalnym GCC (4.7, dzięki uprzejmości rubenvb). GCC na ideone drukuje kilka "zaimplementowanych" wewnętrznych błędów kompilatora.

Musiałem wprowadzić "szczegóły implementacji" klasy Experiment do publicznej wiadomości, ponieważ z pewnych powodów (które pachną jak błąd), moja wersja GCC narzeka, że ​​są prywatne, mimo że używa ich tylko klasa.

#include <utility> 

template<typename T, typename Ignored> 
struct Ignore { typedef T type; }; 

struct EatAll { 
    template<typename ...T> 
    EatAll(T&&...) {} 
}; 

template<typename T> 
struct Experiment : T { 
public: 
    typedef char yes[1]; 
    typedef char no[2]; 

    static void check1(T const&); 
    static void check1(EatAll); 

    // if this SFINAE fails, T accepts it 
    template<typename ...U> 
    static auto check(int, U&&...u) 
    -> typename Ignore<no&, 
     decltype(Experiment::check1({std::forward<U>(u)...}))>::type; 

    template<typename ...U> 
    static yes &check(long, U&&...); 

public: 
    void f() {} 
    template<typename ...U, 
      typename std::enable_if< 
      std::is_same<decltype(Experiment::check(0, std::declval<U>()...)), 
          yes&>::value, int>::type = 0> 
    Experiment(U &&...u):T{ std::forward<U>(u)... } 
    {} 
}; 

// TEST 

struct AbstractBase { 
    protected: 
    AbstractBase(int, float); 
    virtual void f() = 0; 
}; 

struct Annoyer { Annoyer(int); }; 

void x(Experiment<AbstractBase>); 
void x(Annoyer); 

int main() { 
    x({42}); 
    x({42, 43.f}); 
} 

Aktualizacja: Kod działa również na Clang.

+0

Bardzo inteligentne użycie niejednoznaczności, muszę przyznać. Pozwól mi sprawdzić, czy działa w moim ustawieniu. A propos, jak sobie radzi z prywatnymi konstruktorami bazowymi? –

+0

@GrzegorzHerman prywatne konstruktory bazowe nie są sprawdzane. Muszę przyznać, że :(Więc pomyśli, że konwersja jest możliwa –

+0

to jest to, co myślałem - ale jest to dla mnie niewielki problem, najważniejsze jest to, aby to działało dla klas abstrakcyjnych :) –

Powiązane problemy