交替类型的参数包

时间:2018-12-11 18:01:06

标签: c++ templates parameters variadic-templates pack

我有一个struct C,它使用可变数量的struct Astruct 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 :(得分:6)

您可以使用可变参数模板和SFINAE来仅启用类型参数满足您(或任意任意)条件的构造函数。

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

type_traitsstd::false_type需要std::true_type

alternates模板是关键。目标是使alternates<X, Y, T1, T2, T3, ..., Tn>std::true_type继承,并且仅当T1,... Tn列表交替XY时。默认选择(仅在下面)是 no ,但是我们专门研究匹配的情况。

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的其他任何列表也会替换。

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

struct C
{

我们将构造函数定义为模板,该模板首先获取参数包(类型将被推导,调用时无需指定),并且具有用于SFINAE的默认模板参数。如果无法基于参数包计算默认参数,则构造函数将不存在。我在示例中添加了关于对数的额外条件。

    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为true时,std::enable_if<condition, T>::type仅定义::typecondition部分)。那可以是在编译​​时可计算的任意布尔表达式。如果为假,则最后说::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
}

Try the code here.

答案 1 :(得分:5)

我想您可以使用模板委托构造函数

以下内容

#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&&)
    { }

答案 2 :(得分:0)

也许这样会有所帮助...

#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;
}

Working Code

-输出-

1 2.3
4 9.2
5 3.5

3.14 a
6.95 b
2.81 c
8.22 d

-注意-我没有为odd数量的参数合并。有关奇数情况的更详细的解决方案,您可以使用SFINAEDelegating Constructor来参考其他答案。