使函数接受其参数列表的任何排列的规范方法是什么?

时间:2011-08-16 01:48:01

标签: c++ function parameters optional-parameters

假设我有

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);

这样,如果我想使用默认参数,只允许我省略cbc或其中所有三个 - 但不仅仅是仅ab。但是,由于参数类型不能混淆,所以调用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个排列)的界限之间变得乏味。

有没有办法自动获取此功能,而不必实际执行所有重载,例如通过变量参数列表或某种模板元编程?

4 个答案:

答案 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

它的工作原理基本上是将“命名”参数转换为可以创建和分配的类型。