假设我有
class A,B,C;
const A a_def;
const B b_def;
const C c_def;
void f(A a=a_def, B b=b_def, C c=c_def);
这样,如果我想使用默认参数,只允许我省略c
或b
和c
或其中所有三个 - 但不仅仅是仅a
或b
。但是,由于参数类型不能混淆,所以调用f(A(), C())
(或实际上f(B(), C(), A())
)是完全没有意义的:参数的顺序是任意的,实际上没有意义。)
为了启用这些调用函数的替代方法,我现在倾向于手动重载每个排列
void f(A a, C c, B b=b_def) { f(a,b,c); }
void f(B b, A a=a_def, C c=c_def) { f(a,b,c); }
void f(B b, C c, A a=a_def) { f(a,b,c); }
void f(C c, A a=a_def, B b=b_def) { f(a,b,c); }
void f(C c, B b, A a=a_def) { f(a,b,c); }
只有三个参数(3!= 6个排列)是可以接受的,但在四个(4!= 24个排列)和五个参数(5!= 120个排列)的界限之间变得乏味。
有没有办法自动获取此功能,而不必实际执行所有重载,例如通过变量参数列表或某种模板元编程?
答案 0 :(得分:2)
创建一个结构来传递参数。
class Params{
public:
Params();// set defaults
Params& A(int);
Params& B(int);
int a,b;
};
然后致电
f(Params().A(5));
答案 1 :(得分:1)
恕我直言,通常最好的解决方案是定义一个传递参数的结构。
但是你声明你不想要那个,你想要正常的用法表示法。
在这种情况下,Boost参数库最简单。
如果你不能使用Boost,你可以自己动手。
然而,DIY“Boost Parameters”类似解决方案的代码量,参数数量的二次方,虽然二次方比要好得多,但它仍然有点令人望而却步......我在下面给出一个代码示例。主要思想是按类型识别参数,然后在编译时轻松排序。
template< class Type >
struct Pointer
{
Type const* p_;
Pointer( Type* p = 0 ): p_( p ) {}
};
template< class Type, class Args >
Type const* pointer( Args const& args )
{
return static_cast< Pointer< Type > const& >( args ).p_;
}
template< class Type, class Args >
Type const*& pointerRef( Args& args )
{
return static_cast< Pointer< Type >& >( args ).p_;
}
//-----------------------------------------------------
class A {}; class B {}; class C {};
A const a_def; B const b_def; C const c_def;
void foo( A const* pa, B const* pb, C const* pc )
{
A const& a = (pa? *pa : a_def);
B const& b = (pb? *pb : b_def);
C const& c = (pc? *pc : c_def);
// Whatever, use a b c here.
}
struct FooArgs
: Pointer< A >
, Pointer< B >
, Pointer< C >
{};
void foo( FooArgs const& args )
{
foo( pointer< A >( args ), pointer< B >( args ), pointer< C >( args ) );
}
void foo()
{
foo( FooArgs() );
}
template< class T1 >
void foo( T1 const& v1 )
{
FooArgs args;
pointerRef< T1 >( args ) = &v1;
foo( args );
}
template< class T1, class T2 >
void foo( T1 const& v1, T2 const& v2 )
{
FooArgs args;
pointerRef< T1 >( args ) = &v1;
pointerRef< T2 >( args ) = &v2;
foo( args );
}
template< class T1, class T2, class T3 >
void foo( T1 const& v1, T2 const& v2, T3 const& v3 )
{
FooArgs args;
pointerRef< T1 >( args ) = &v1;
pointerRef< T2 >( args ) = &v2;
pointerRef< T3 >( args ) = &v3;
foo( args );
}
int main()
{
foo( B() );
}
如上所述,DIY的东西将是我的最后选择。
我省略了此示例代码中的错误检查。例如,如果两个或多个实际参数属于同一类型,则代码不应编译。我也省略了对形式参数不是全部相同类型的情况的概括。这些遗漏的东西会增加复杂性。因此,如果上述内容似乎令人望而却步,那么请考虑一下“完整版”中的情况,可以这么说。
对于需要严格键入的可选参数的情况,例如类层次结构中的构造函数,请参阅我的博客"How to do typed optional arguments in C++98"。
干杯&amp;第h。,
答案 2 :(得分:0)
混合使用C ++ 0x和一点Boost(后者可以轻松替换):
typedef boost::variant<A, B, C> Arg;
void f(std::initializer_list<Arg> args)
{
A *a = NULL;
B *b = NULL;
C *c = NULL;
// for each arg, assign to a, b, or c,
// possibly throwing if one type is used multiple times
if (!a)
a = new A();
// and similar for B and C, or use smarter pointer types
f(*a, *b, *c);
}
这样称呼,这只是比它应该稍微丑陋:
f({C(), B()});
我没有对此进行测试,但我认为它可以正常工作。
答案 3 :(得分:0)
查看Boost.Parameters库。它使用一些模板重物来使其工作。 http://www.boost.org/doc/libs/1_37_0/libs/parameter/doc/html/index.html
它的工作原理基本上是将“命名”参数转换为可以创建和分配的类型。