What is the canonical way to make a function accept any permutation of its argument list?
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) { 开发者_JS百科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?
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));
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.,
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.
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.
精彩评论