Обнаружение защищенных конструкторов (возможно, абстрактного) базового класса
Я экспериментирую с новыми функциями С++ 11. В моей настройке я бы очень хотел использовать наследующие конструкторы, но, к сожалению, компилятор пока не реализовал их. Поэтому я пытаюсь моделировать одно и то же поведение. Я могу написать что-то вроде этого:
template <class T>
class Wrapper : public T {
public:
template <typename... As>
Wrapper(As && ... as) : T { std::forward<As>(as)... } { }
// ... nice additions to T ...
};
Это работает... большую часть времени. Иногда код, использующий класс Wrapper
, должен использовать SFINAE для определения того, как можно построить такой Wrapper<T>
. Однако существует проблема: в отношении разрешения перегрузки конструктор Wrapper<T>
принимает любые аргументы, но компиляция не выполняется (и это не рассматривается SFINAE), если тип T
не может быть построен с использованием те.
Я пытался условно включить различные экземпляры шаблона конструктора, используя enable_if
template <typename... As, typename std::enable_if<std::is_constructible<T, As && ...>::value, int>::type = 0>
Wrapper(As && ... as) // ...
который отлично работает до тех пор, пока:
- соответствующий конструктор
T
равен public
-
T
не является абстрактным.
Мой вопрос: как избавиться от вышеуказанных двух ограничений?
Я попытался преодолеть первое, проверив (используя SFINAE и sizeof()
), правильно ли сформировано выражение new T(std::declval<As &&>()...)
внутри Wrapper<T>
. Но это, конечно, не работает, потому что единственный способ, которым производный класс может использовать свой конструктор, защищенный базой, находится в списке инициализации члена.
Для второго я понятия не имею, и это тот, который мне нужен больше, потому что иногда это Wrapper
, который реализует абстрактные функции T
, делая его полным.
Мне нужно решение, которое:
- является правильным в соответствии со стандартом
- работает в любом из gcc-4.6. *, gcc-4.7. * или clang-3. *
Спасибо!
Ответы
Ответ 1
Это, похоже, отлично работает на моем местном GCC (4.7, любезно предоставлено rubenvb). Однако GCC на ideone печатает несколько "реализованных" внутренних ошибок компилятора.
Мне пришлось составлять "детали реализации" класса Experiment
public, потому что по некоторым причинам (который пахнет ошибкой) моя версия GCC жалуется на то, что они являются закрытыми, хотя только сам класс использует ее.
#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});
}
Обновление: код также работает на Clang.