![]() |
Overloaded function dilemma
For a series of classes I'm making it would be nice if they had constructors
that accept: - an interator range, - an initializer list, and - a variable amount of arguments (using a variadic template) The first two are trivial, but the third one clashes badly with the first one. The third constructor is not absolutely mandatory, but it would be a nice-to-have feature. However, I must admit that regardless of my C++ experience, I don't know if it could be even theoretically possible to have both the first and the third constructors at the same time. The problem is that the parameter types can be anything (or, more precisely, this is a templated class whose template type can be anything, and the variadic constructor would take parameters of that type). The parameters to the variadic constructor can very well be pointers. Therefore I don't see any way of distinguishing between the first and the third constructor: If you give the constructor two pointers, it matches both constructor overloads. Any ideas if this could even theoretically be possible? |
Re: Overloaded function dilemma
On Tuesday, October 16, 2012 9:16:38 AM UTC+2, Juha Nieminen wrote:
> For a series of classes I'm making it would be nice if they had constructors > > that accept: > > > > - an interator range, > > - an initializer list, and > > - a variable amount of arguments (using a variadic template) > > > > The first two are trivial, but the third one clashes badly with the first > > one. The third constructor is not absolutely mandatory, but it would be a > > nice-to-have feature. However, I must admit that regardless of my C++ > > experience, I don't know if it could be even theoretically possible to > > have both the first and the third constructors at the same time. > > > > The problem is that the parameter types can be anything (or, more precisely, > > this is a templated class whose template type can be anything, and the > > variadic constructor would take parameters of that type). The parameters > > to the variadic constructor can very well be pointers. > > > > Therefore I don't see any way of distinguishing between the first and the > > third constructor: If you give the constructor two pointers, it matches > > both constructor overloads. > > > > Any ideas if this could even theoretically be possible? This one seems a possibility: #include <iostream> #include <tuple> struct VariadicX { VariadicX( char* begin, char* end ) { std::cout << "1 called" << std::endl; } VariadicX( std::initializer_list<char> ) { std::cout << "2 called" << std::endl; } template <class ... Types> VariadicX( std::tuple<Types...>&& t ) { std::cout << "3 && called" << std::endl; } }; int main() { char array[] = { 'H', 'a', 'l', 'l', 'o' }; VariadicX( array, array+sizeof(array) ); VariadicX( { 'H', 'a', 'l', 'l', 'o' } ); char *x = 0, *y = 0; VariadicX( std::make_tuple( x, 10 ) ); VariadicX( std::make_tuple( x, y ) ); return 0; } |
Re: Overloaded function dilemma
On 10/16/2012 3:16 AM, Juha Nieminen wrote:
> For a series of classes I'm making it would be nice if they had constructors > that accept: > > - an interator range, > - an initializer list, and > - a variable amount of arguments (using a variadic template) > > The first two are trivial, but the third one clashes badly with the first > one. The third constructor is not absolutely mandatory, but it would be a > nice-to-have feature. However, I must admit that regardless of my C++ > experience, I don't know if it could be even theoretically possible to > have both the first and the third constructors at the same time. > > The problem is that the parameter types can be anything (or, more precisely, > this is a templated class whose template type can be anything, and the > variadic constructor would take parameters of that type). The parameters > to the variadic constructor can very well be pointers. > > Therefore I don't see any way of distinguishing between the first and the > third constructor: If you give the constructor two pointers, it matches > both constructor overloads. > > Any ideas if this could even theoretically be possible? I don't have an answer on the theoretical possibility, sorry. I am however thinking that you can often overcome some language limitations imposed on constructors by using named functions (static members that return an object). Similar to class Foo { public: template<class It> Foo(It i1, It i2); // pair of iterators // instead of another c-tor - a named "maker" function static Foo make_from_many( ... ); }; ... Foo foo(Foo::make_from_many( <whatever> )); I know it may not always be desirable, but it's clear and often actually solves the ambiguity problem since you can distinguish between the construction methods yourself instead of relying on the compiler. V -- I do not respond to top-posted replies, please don't ask |
Re: Overloaded function dilemma
Juha Nieminen wrote:
> For a series of classes I'm making it would be nice if they had > constructors that accept: > > - an interator range, > - an initializer list, and > - a variable amount of arguments (using a variadic template) > > The first two are trivial, but the third one clashes badly with the first > one. The third constructor is not absolutely mandatory, but it would be a > nice-to-have feature. However, I must admit that regardless of my C++ > experience, I don't know if it could be even theoretically possible to > have both the first and the third constructors at the same time. > > The problem is that the parameter types can be anything (or, more > precisely, this is a templated class whose template type can be anything, > and the variadic constructor would take parameters of that type). The > parameters to the variadic constructor can very well be pointers. > > Therefore I don't see any way of distinguishing between the first and the > third constructor: If you give the constructor two pointers, it matches > both constructor overloads. > > Any ideas if this could even theoretically be possible? It appears that your problem is that you are trying to implement a design which inevitably will introduce a series ambiguity problems. Once you abandon the variadic template idea, you will solve your problem. If you are looking for clever ways to pass a a long list of arguments through a constructor, there are far better ways to pull this off, which don't require that you lose time writing an endless list of constructors for each object. As a suggestion, consider the following idea: - On your main classes (i.e., the ones which you intended to add those variadic templates), you only define three types of constructor: one taking an iterator range, one taking an initializer list, and one taking an object of a class defined specifically to pass parameters. - You implement each parameter-passing class as a named parameter idiom. Here's an example: <code> #include <iostream> // the main class class Foo { private: int m_a; float m_b; public: // parameter class, implementing a named parameter idiom struct Param { int a; float b; Param & optionA(int m_a) { a = m_a; return *this; } Param & optionB(float m_b) { b = m_b; return *this; } }; public: Foo(Param const ¶ms) { member1(params.a); member2(params.b); } void member1(int a) { m_a = a; } void member2(int b) { m_b = b; } friend std::ostream & operator << (std::ostream &os, Foo const &); }; std::ostream & operator << (std::ostream &os, Foo const &foo) { return os << "{ a: " << foo.m_a << ", " << foo.m_b << "}"; } int main(void) { using namespace std; Foo a( Foo::Param().optionA(1).optionB(2.0) ); Foo b( Foo::Param().optionB(3.0) ); Foo c( Foo::Param().optionA(5) ); Foo d( Foo::Param().optionB(9.0).optionA(5) ); cout << a << endl; cout << b << endl; cout << c << endl; cout << d << endl; return 0; } </code> If you still wish to use a variadic template constructor, you can pull this off on the Foo::Param class, which won't clash with any of Foo's constructors. Hope this helps, Rui Maciel |
Re: Overloaded function dilemma
On Tuesday, October 16, 2012 4:04:42 PM UTC+2, Rui Maciel wrote:
> If you are looking for clever ways to pass a a long list of arguments > > through a constructor, there are far better ways to pull this off, which > > don't require that you lose time writing an endless list of constructors for > > each object. As a suggestion, consider the following idea: > > > > - On your main classes (i.e., the ones which you intended to add those > > variadic templates), you only define three types of constructor: one taking > > an iterator range, one taking an initializer list, and one taking an object > > of a class defined specifically to pass parameters. [snip] Having an object for specifically passing parameters implies that for handling new parameter types, the object interface requires modification (except when that object is something like a tuple that takes arbitrary amount of arguments/type). If the class should really be able to handle an arbitrary number of arguments of varying types, what better object than tuple in combination with variadic templates (or using a static named function is also a good idea Imho)? Kind regards, Werner |
Re: Overloaded function dilemma
Werner wrote:
> Having an object for specifically passing parameters implies > that for handling new parameter types, the object > interface requires modification (except when that object > is something like a tuple that takes arbitrary amount of > arguments/type). I'm not sure I understood what you meant by that. Could you please provide an example where implementing support for a new parameter doesn't require an interface to be modified? > If the class should really be able to handle an arbitrary > number of arguments of varying types, what better > object than tuple in combination with variadic templates > (or using a static named function is also a good idea Imho)? A tupple is actually a poor alternative, as it is nothing more than an ad- hoc definition of an aggregate type, which is essentially what a parameter class is, that introduces problems regarding ambiguity and lack of expressiveness. Rui Maciel |
Re: Overloaded function dilemma
On 16/10/2012 09:16, Juha Nieminen wrote:
> For a series of classes I'm making it would be nice if they had constructors > that accept: > > - an interator range, > - an initializer list, and > - a variable amount of arguments (using a variadic template) > > The first two are trivial, but the third one clashes badly with the first > one. The third constructor is not absolutely mandatory, but it would be a > nice-to-have feature. However, I must admit that regardless of my C++ > experience, I don't know if it could be even theoretically possible to > have both the first and the third constructors at the same time. > > The problem is that the parameter types can be anything (or, more precisely, > this is a templated class whose template type can be anything, and the > variadic constructor would take parameters of that type). The parameters > to the variadic constructor can very well be pointers. > > Therefore I don't see any way of distinguishing between the first and the > third constructor: If you give the constructor two pointers, it matches > both constructor overloads. > > Any ideas if this could even theoretically be possible? Can you show us the code giving you the problems you described? |
Re: Overloaded function dilemma
Juha Nieminen <nospam@thanks.invalid> wrote:
> For a series of classes I'm making it would be nice if they had constructors > that accept: > > - an interator range, > - an initializer list, and > - a variable amount of arguments (using a variadic template) From a more hypothetical point, in theory it could be possible to distinguish between an iterator range and a variable number of arguments because in the latter case all the arguments have to be compatible with the type accepted by the class. In other words, if you have something like this: template<typename Value_t> class MyClass { public: // All parameters have to be compatible with Value_t: template<typename... Params> MyClass(Params&&...); // If two paramers are given and they are *not* compatible with // Value_t, but *b and *e are compatible with Value_t, then this // ought to be called: template<typename InputIterator> MyClass(InputIterator b, InputIterator e); }; In principle if you give the constructor values of type Value_t (or anything that's compatible with it, such as an object of a class derived from Value_t, if Value_t is a pointer to an object), that's distinguishable from two iterators which are not themselves compatible with Value_t, but their dereferences are. The standard library container classes usually do some template magic to distinguish between integrals and iterator ranges. For example std::vector<int> has a constructor that takes an iterator range and another constructor that takes two integrals. If you call the constructor with to ints, for example, it will match the iterator range constructor (because it's a better match than the other constructor, which takes a size_t and an int). However, std::vector still manages to do the right thing, thanks to some template magic. I was thinking that perhaps, possibly, the same kind of template trickery could be used in this case. |
Re: Overloaded function dilemma
Juha Nieminen wrote:
> For a series of classes I'm making it would be nice if they had constructors > that accept: > > - an interator range, > - an initializer list, and > - a variable amount of arguments (using a variadic template) > > The first two are trivial, but the third one clashes badly with the first > one. The third constructor is not absolutely mandatory, but it would be a > nice-to-have feature. However, I must admit that regardless of my C++ > experience, I don't know if it could be even theoretically possible to > have both the first and the third constructors at the same time. Why, yes, sfinae lets you do that. You can for instance disable the last one when it has 2 arguments that are iterators, where being an iterator is detected on good platforms by looking at iterator_traits and on others by being a pointer or having the right typedefs (iterator_category, etc), and disable the first one when its arguments are not iterators. Or if the variadic template can't take anything but say types convertible to some other type, you can also use that. |
| All times are GMT. The time now is 11:04 PM. |
Powered by vBulletin®. Copyright ©2000 - 2013, vBulletin Solutions, Inc.
SEO by vBSEO ©2010, Crawlability, Inc.