Различный базовый класс в зависимости от параметров шаблона - PullRequest
3 голосов
/ 04 октября 2019

Можно ли определить во время компиляции точную базу шаблона класса в зависимости от его параметров? Например, у меня есть шаблон класса, который принимает один аргумент в своем конструкторе, и я хочу расширить этот класс другим аргументом, для которого будет использоваться другой конструктор. Проблема заключается в том, что этот второй экземпляр (с двумя аргументами) должен иметь другой базовый класс, чем класс с одним параметром. Я могу определить правильный базовый класс с помощью std::conditional, но проблема заключается в наличии обоих конструкторов в одном шаблоне класса. Например:

#include <type_traits>

struct X
{
};

struct Y
{
};

struct XX
{
};

struct XY
{
};


template<class T, class V = void>
struct Z: public std::conditional_t<std::is_void_v<V>, XX, XY>
{
    Z(T&& t, V&& v)
        : XY()
    {
        // do smth with t and v
    }

    Z(T&& t)
        : XX()
    {
        // do smth with t
    }
};

int main()
{
    auto a = Z(X(), Y());
    auto b = Z(X());         // <-- this instantiation fails
}

Здесь Z(X(), Y()) работает, но для Z(X()) происходит сбой с ошибкой компиляции:

main.cpp: In instantiation of 'struct Z<X, void>':

main.cpp:39:19:   required from here

main.cpp:23:5: error: forming reference to void

   23 |     Z(T&& t, V&& v)

      |     ^

Обновление:

Пытался сделать шаблон с двумя аргументами enable_if, но он не работает (такой же forming reference to void, как в исходном коде):

    template<class = std::enable_if<! std::is_void_v<V>>>
    Z(T&& t, V&& v)
        : XY()
    {
        // do smth with t and v
    }

Ответы [ 2 ]

2 голосов
/ 05 октября 2019

Не элегантное решение, но, кажется, может работать , использовать частичную специализацию и руководство по выводам.

template<class T, class V>
struct Z: public XY
{
    Z(T&& t, V&& v)
        : XY()
    {
        // do smth with t and v
    }
};

template<class T>
struct Z<T, void> : public XX
{
    Z(T&& t)
    : XX()
    {

    }
};

template <class T>
Z(T&& ) -> Z<T, void>;
0 голосов
/ 07 октября 2019

Обычный подход для расширенного определения условного класса - это зависимый базовый класс:

struct B1 {};
struct B2 {};

template<class T>
struct Z1 : B1 {
  Z1(T&&) : B1() {}
};
template<class T,class V>
struct Z2 : B2 {
  Z2(T&&,V&&) : B2() {}
};

template<class T,class V>
using Z0=std::conditional_t<std::is_void_v<V>,Z1<T>,Z2<T,V>>;

template<class T,class V=void>
struct Z : Z0<T,V> {
  using Z0<T,V>::Z0;
};
template<class T> Z(T) -> Z<T>;
template<class T,class V> Z(T,V) -> Z<T,V>;

Руководства по выводам необходимы, поскольку для CTAD рассматривается только первичный шаблон (без его унаследованных конструкторов).

Если Z, полученное в результате этого преобразования, не имеет собственных членов, C ++ 20 может предложить возможность использовать (то, что здесь называется) Z0 напрямую, поскольку он поддерживает CTAD для шаблонов псевдонимов.

...