#c #declaration #friend #typetraits
#c #объявление #друг #признаки типов
Вопрос:
Я играю с одноэлементным шаблоном в C и хочу реализовать глобальную функцию, которая завершает построение класса. Я использовал std::is_base_of
в этой функции, но это делает невозможным объявление функции в классе.
Вот короткий пример:
#include <type_traits>
class A {};
template<typename T>
typename std::enable_if_t<std::is_base_of_v<A, T>, T*>
Instance() { return T(); }
template<typename T>
typename std::enable_if_t<!std::is_base_of_v<A, T>, T*>
Instance() { return T(); }
class B : public A {
protected:
B();
friend B* Instance<B>(); // Error
};
Приведенный выше код приведет к «недопустимому использованию неполного типа» с использованием gcc или C2139 с использованием MSVC при создании экземпляра первой функции.
Итак, кроме того, чтобы сделать конструктор B::B()
общедоступным, есть ли какой-либо возможный способ для меня обойти это?
Ответ №1:
Проблема в том, что во время определения класса класс все еще неполный,
и std::is_base_of
требуется полный тип для Derived
, иначе у вас есть UB.
Если у вас есть доступ к C 17, вы можете сделать:
template<typename T>
T* Instance() {
if constexpr (std::is_base_of_v<A, T>) {
return nullptr; // Your impl
} else {
return nullptr; // Your impl
}
}
class B : public A {
protected:
B();
friend B* Instance<B>();
};
Комментарии:
1. Это решение требует наименьшей модификации моего предыдущего кода. Спасибо!
Ответ №2:
Вы можете использовать функтор шаблона для создания объектов целевого класса. Этот функтор должен быть другом целевого класса. Смотрите пример ниже.
#include <type_traits>
#include <tuple>
class A {};
template <typename T>
struct Instance_t
{
T* operator () () const
{
if constexpr (std::is_base_of_v<A, T>)
{
return new T();
}
else
{
return nullptr;
}
}
};
template <typename T>
constexpr Instance_t<T> Instance{};
class B : public A {
protected:
B()
{}
template <typename T> friend struct Instance_t;
};
int main()
{
auto b=Instance<B>();
std::ignore=b;
return 0;
}
Проверьте демонстрацию.
Ответ №3:
Кажется, я могу просто использовать статическую функцию-член (например B::Instance()
) для достижения одноэлементного шаблона и использовать конструктор для завершения определенных функций…
Код:
#include <type_traits>
class A {
protected:
A() {
// Do something special here
}
};
class B : public A {
protected:
B();
public:
static B* Instance() {
static B* b = new B();
return b;
}
};