Пакет параметров с чередующимися типами - PullRequest
0 голосов
/ 11 декабря 2018

У меня есть struct C, который инициализируется с переменным числом экземпляров struct A и struct B.Например:

struct A
{};

struct B
{};

struct C
{
    C(A&& o1, B&& p1, A&& o2)
    {}
    C(A&& o1, B&& p1, A&& o2, B&& p2, A&& o3)
    {}
    C(A&& o1, B&& p1, A&& o2, B&& p2, A&& o3, B&& p3, A&& o4)
    {}
    C(A&& o1, B&& p1, A&& o2, B&& p2, A&& o3, B&& p3, A&& o4, B&&p4, A&& o5)
    {}
};

Итак, вместо того, чтобы предоставлять несколько ctor-ов с разным количеством параметров, я хотел бы найти что-то общее.Однако число параметров ctor всегда увеличивается примерно на два параметра: B&& и A&&.Может ли это быть достигнуто с помощью пакетов параметров.Или было бы другое решение без реализации для каждого количества параметров соответствующего ctor?

Цель должна состоять в том, чтобы struct C можно было построить, как в следующих примерах:

C c1 = { A(), B(), A() };
C c2 = { A(), B(), A(), B(), A(), B(), A() };

и т. Д.

Ответы [ 3 ]

0 голосов
/ 11 декабря 2018

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

Что-то вроде следующего:

#include <utility>

struct A {};
struct B {};

struct C
 {
   C (A &&)
    { }

   template <typename ... Ts>
   C (A &&, B &&, Ts && ... ts) : C(std::forward<Ts>(ts)...)
    { }
 };

int main()
 {
   C(A{});
   C(A{}, B{}, A{});
   C(A{}, B{}, A{}, B{}, A{});
   C(A{}, B{}, A{}, B{}, A{}, B{}, A{});
 }

Если вам требуется как минимум три элемента (поэтому не C(A{}), но как минимум C(A{}, B{}, A{}))конструктор без шаблона становится

   C (A &&, B &&, A&&)
    { }
0 голосов
/ 11 декабря 2018

Может быть, что-то подобное поможет ...

#include <iostream>
#include <vector>
#include <utility>

// Simple classes A & B to represent your alternating pattern classes.
class A{ public: int a; };
class B{ public: int b; };

// helper class template to act as a single parameter kind of like std::pair...
template<typename T, typename U>
class Pack{
private:
    T t_;
    U u_;

public:
    Pack( T&& t, U&& u ) : 
      t_( std::move( t ) ),
      u_( std::move( u ) )
    {}

    T getT() const { return t_; }
    U getU() const { return u_; }        
};

// your class with varying amount of parameters for its ctors
template<class T, class U>
class  C{
private:
    std::vector<Pack<T,U>> packs_;

public:
    template<typename... Packs>
    C( Packs&&... packs  ) : packs_{ std::move( packs )... } { }

    std::vector<Pack<T,U>> getPacks() const {
        return packs_;
    }
};

// A few overloaded ostream operator<<()s for easy printing...
std::ostream& operator<<( std::ostream& os, const A& a ) {
    os << a.a;
    return os;
}

std::ostream& operator<<( std::ostream& os, const B& b ) {
    os << b.b;
    return os;
}

template<typename T, typename U>
std::ostream& operator<<( std::ostream& os, const Pack<T,U>& pack ) {
    os << pack.getT() << " " << pack.getU() << '\n';
    return os;
}

// Main program to demonstrate its use
int main() {    
    Pack<int,double> p1( 1, 2.3 ), p2( 4, 9.2 ), p3( 5, 3.5 );        
    C<int, double> c( p1, p2, p3 );    
    for (auto& p : c.getPacks() )
        std::cout << p;

    std::cout << '\n'; 

    Pack<float, char> p4( 3.14f, 'a' ), p5( 6.95f, 'b' ),
                      p6( 2.81f, 'c' ), p7( 8.22f, 'd' );
    C<float, char> c2( p4, p5, p6, p7 );
    for ( auto& p : c2.getPacks() )
        std::cout << p;    

    return 0;
}

Рабочий код

-Выход-

1 2.3
4 9.2
5 3.5

3.14 a
6.95 b
2.81 c
8.22 d

-Примечание- Я не включил ни одного odd количества параметров.Для более подробного решения в нечетных случаях вы можете обратиться к другим ответам с помощью SFINAE или Delegating Constructor.

0 голосов
/ 11 декабря 2018

Вы можете использовать шаблон переменной и SFINAE, чтобы включить только конструктор, в котором параметры типа удовлетворяют вашему (или любому произвольному) условию.

#include <type_traits>
struct A {};
struct B {};

Вам необходимо type_traits для std::false_type и std::true_type.

Шаблон alternates является ключом.Цель состоит в том, чтобы alternates<X, Y, T1, T2, T3, ..., Tn> наследовать от std::true_type тогда и только тогда, когда список T1, ... Tn чередуется X и Y.По умолчанию (чуть ниже) выбрано нет , но мы специализируемся на соответствующих случаях.

template <typename X, typename Y, typename... Ts>
struct alternates : std::false_type {};

Я решил сделать этот шаблон более универсальным, чем здесь требуется, и разрешить alternates<X, Y>наследовать от true_type.Пустой список удовлетворяет математическому требованию, чтобы все его элементы чередовались.Это будет хорошим временным ограничением для рекурсивного определения ниже.

template <typename X, typename Y>
struct alternates<X, Y> : std::true_type {};

Любой другой список alternates<X, Y, Ts...> чередуется тогда и только тогда, когда Ts... минус первый элемент чередуется Y и X (Y first!).

template <typename X, typename Y, typename... Ts>
struct alternates<X, Y, X, Ts...>
: alternates<Y, X, Ts...> {};

struct C
{

Мы определяем конструктор как шаблон, который сначала принимает пакет параметров (тип будет выведен, указывать при вызове нет необходимости), и он имеет параметр шаблона по умолчанию дляСФИНАЕ целей.Если аргумент по умолчанию не может быть рассчитан на основе пакета параметров, конструктор не будет существовать.Я добавил дополнительные условия о количестве пар, которые я предполагал из примера.

    template<typename... Ts,
        typename = typename std::enable_if<
            sizeof...(Ts) % 2 == 1 &&
            sizeof...(Ts) >= 3 && // did you imply this?
            alternates<A, B, Ts...>::value
        >::type>
    C(Ts&&...);
};

Способ SFINAE работает так: std::enable_if определяет только std::enable_if<condition, T>::type (::type часть), если condition верно.Это может быть любое произвольное логическое выражение, вычисляемое во время компиляции.Если оно ложно, то, сказав ::type в конце, вы получите ошибку замены , и перегрузка, в которой вы пытались его использовать (например, C{A(), A(), A()}), просто не будет определена.

Вы можете проверить, что приведенные ниже примеры работают должным образом.Закомментированные не будут работать.

int main() {
    C c1 { A(), B(), A() };
    C c2 { A(), B(), A(), B(), A(), B(), A() };
    // C c3 {};                     // I assumed you need at least 2
    // C c4 { A(), B(), A(), A() }; // A, A doesn't alternate
    // C c5 { B(), A(), B() };      // B, A, B not allowed
    // C c6 { A(), B(), A(), B() }; // A, B, A, B doesn't pair
}

Попробуйте код здесь.

...