Overloaded function dilemma

J

Juha Nieminen

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?
 
W

Werner

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

Victor Bazarov

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
 
R

Rui Maciel

Juha said:
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 &params)
{
member1(params.a);
member2(params.b);
}

void member1(int a)
{
m_a = a;
}

void member2(int b)
{
m_b = b;
}

friend std::eek:stream & operator << (std::eek:stream &os, Foo const &);
};


std::eek:stream & operator << (std::eek:stream &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
 
W

Werner

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
 
R

Rui Maciel

Werner said:
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
 
L

Luca Risolia

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?
 
J

Juha Nieminen

Juha Nieminen said:
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.
 
M

Marc

Juha said:
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.
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

Forum statistics

Threads
473,990
Messages
2,570,211
Members
46,796
Latest member
SteveBreed

Latest Threads

Top