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

发布于 2024-11-29 21:35:56 字数 823 浏览 1 评论 0原文

假设我有

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 排列)。

有没有什么方法可以自动获得此功能,而无需实际执行所有重载,例如通过变量参数列表或某种模板元编程?

Suppose I have

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

This does, if I want to use the default parameters, only allow me to omit either just c, or b and c, or all three of them – but not just a or b alone. However, as the argument types cannot be mixed up, it would be completely unabiguous to call f(A(), C()), (or in fact f(B(), C(), A()): the order of arguments is arbitrary and actually meaningless).

To enable these alternative ways of calling the function, I now tend to overload every permutation manually

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

which is acceptable for just three parameters (3!=6 permutations) but gets tedious at four (4!=24 permutations) and out of bounds at five parameters (5!=120 permutations).

Is there any way to get this functionality automatically, without actually having to do all the overloads, for instance by means of variable argument lists or some kind of template metaprogramming?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(4

初懵 2024-12-06 21:35:56

创建一个结构体来传递参数。

class Params{
 public:
   Params();// set defaults
   Params& A(int);
   Params& B(int);
   int a,b;
};

然后打电话

 f(Params().A(5));

Create a struct to pass the parameters.

class Params{
 public:
   Params();// set defaults
   Params& A(int);
   Params& B(int);
   int a,b;
};

then call

 f(Params().A(5));
摇划花蜜的午后 2024-12-06 21:35:56

恕我直言,通常最好的解决方案是定义一个结构来传递参数。

但你声明你不想要那样,你想要正常的使用符号。

在这种情况下,Boost 参数库是最简单的。

如果你不能使用Boost,你可以自己做。

然而,DIY 类似“Boost 参数”的解决方案的代码量是参数数量的二次方,尽管二次方比阶乘好得多,但它仍然有点令人望而却步……我在下面给出了一个代码示例。主要思想是按类型识别参数,然后在编译时轻松对它们进行排序。

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 的东西将是我最后的选择。

我在此示例代码中省略了错误检查。例如,如果两个或多个实际参数具有相同类型,则不应编译代码。我还省略了对形式参数并非都是同一类型的情况的概括。这些被省略的事情导致了更多的复杂性。因此,如果上述内容看起来令人望而却步,请考虑“完整版本”中的情况,可以这么说。

对于需要严格类型化的可选参数(例如类层次结构中的构造函数)的情况,请参阅我的博客文章 "如何在 C++98 中执行类型化可选参数"

干杯&呵呵,

IMHO the generally best solution is to define a structure to pass the parameters.

But you state that you don't want that, you want normal usage notation.

In that case the Boost Parameters library is simplest.

In case you can't use Boost, you can do it yourself.

However, the amount of code for a DIY "Boost Parameters"-like solution, quadratic in the number of arguments, although quadratic is much better than factorial, it's still sort of prohibitive… I give a code example below. The main idea is to identify arguments by types, and then they're easily sorted at compile time.

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

As mentioned, that DIY thing would be my last choice.

I omitted error checking in this example code. For example, the code should not compile if two or more actual arguments are of the same type. I also omitted generalization to the case where the formal arguments are not all of the same type. These omitted things contribute to more complexity. So if the above seems prohibitive, consider what it would be like in a "full-version", so to speak.

For the case where you need strictly typed optional arguments for e.g. constructors in a class hierarchy, see my blog posting "How to do typed optional arguments in C++98".

Cheers & hth.,

分分钟 2024-12-06 21:35:56

查看 Boost.Parameters 库。它使用一些模板繁重的工作来使其工作。 http://www.boost.org/doc /libs/1_37_0/libs/parameter/doc/html/index.html

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

Look into the Boost.Parameters Library. It uses some template heavy lifting to get it to work. http://www.boost.org/doc/libs/1_37_0/libs/parameter/doc/html/index.html

It works basically by making your 'named' parameters into types that can be created, and assigned.

梦太阳 2024-12-06 21:35:56

混合 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()});

我还没有测试过这个,但我认为它可以工作。

Mixing C++0x and a little bit of Boost (the latter of which you could easily replace):

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

Called like this, which is only slightly uglier than it should be:

f({C(), B()});

I haven't tested this, but I think it can work.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文