我有一个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() };
等
答案 0 :(得分:6)
您可以使用可变参数模板和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
时。默认选择(仅在下面)是 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
仅定义::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
}
答案 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;
}
-输出-
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
来参考其他答案。