From: bf on
On Jul 14, 5:48 pm, bf <bjor...(a)fahller.se> wrote:

> I think a solution can be made with a variadic template constructor,
> which also nicely takes care of multi-parameter constructors, but I
> fail to figure out how to do a std::forward<U...>(u...). Suggestions?

I just figured out a (the?) solution:

template <typename T>
class wrapper
{
public:
template <typename ...U>
wrapper(U&& ...u) : t(std::forward<U>(u)...) {}
private:
T t;
};

Works for any number of parameters that the contructor of T can
accept.
_
/Bjorn

--
[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]

From: SG on
On 14 Jul., 17:48, bf wrote:
> Suppose I need a template that encapsulates an object, and I need the
> ability to construct the template instantiations in all the ways that
> the encapsulated object can be constructed. A possible construction
> may be:
>
> template <typename T>
> class wrapped
> {
> public:
> wrapped() {}
> template <typename U>
> wrapped(U&& u) : t(std::forward<U>(u) {}
> private:
> T t;
> };
>
> Unfortunately it fails if T isn't default constructible.
> [...]
> I think a solution can be made with a variadic template constructor,
> which also nicely takes care of multi-parameter constructors, but I
> fail to figure out how to do a std::forward<U...>(u...). Suggestions?

template <typename T>
class wrapped
{
public:
template <typename... U>
explicit wrapped(U&&... u) : t(std::forward<U>(u)...) {}
private:
T t;
};

I'm not 100% sure if it handles the default construction case but I
think it does.

Cheers!
SG


--
[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]

From: lucdanton on
On Jul 14, 5:48 pm, bf <bjor...(a)fahller.se> wrote:
> QOI detail, or behaviour mandated by proposed standard
> wording?

I ask myself that sort of question quite often. I'm not sure what the
answer when side-effects are present in either the constructor(s) and
destructor. Obviously under the as-if rule an implementation is free
to sort those details out but from what I know of compiler technology
and static analysis this can be tricky for non-trivial constructors/
destructors.

> I think a solution can be made with a variadic template constructor,
> which also nicely takes care of multi-parameter constructors, but I
> fail to figure out how to do a std::forward<U...>(u...). Suggestions?

I typically write a perfect forwarding constructor thus:

template<typename ...U>
some_type(U&& ...u): some_member(std::forward<U>(u)...) {}

Which is, incidentally, what appears in papers for the next standard I
believe. The trick (well, for me anyway) is to write as if the
template were not variadic, and adding ...'s in such a fashion that it
is factored out the furthest. My latest attempt was "variading" the
following:

template<typename T>
struct {
boost::variant<
std::future<T>*,
T
> f;
};

to:

template<typename ...T>
struct {
boost::variant<
std::future<T>*,
T
...> f;
};

Note also that under gcc-4.5 a variadic, perfect-forwarding
constructor will mess copy construction two-fold:

- a custom-defined constructor suppress the default instantiation of
the copy constructor
This is obvious but if you forget it you might not notice it if the
perfectly-forwarded call is valid construct!
The obvious cure is adding the default copy constructor:
some_type(some_type const&) = default; (you can put in delete instead
if your type is non-copyable and you want a more explicit warning when
someone attempts to copy)

- the perfect-forwarding constructor eats reference to non-const.
D'oh !
So assuming you do have a copy constructor:
some_type s;
auto const& cr = s;
some_type(cr); // fine!
some_type(s); // Uh oh! some_type(U&& ...u) gets called with U =
some_typ&!

I don't know if this is intended by the standard or a quirk of gcc-4.5
(rvalue refs 1.0 got redesigned for this kind of gotcha's I believe,
but much worse). I've had some luck disabling that sort of behaviour
with std::enable_if and std::is_same (from <type_traits>) but I'm not
that familiar with template metaprogramming so I don't think I've done
it for _variadic_ perfect-forwarding constructors.


--
[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]

From: Johannes Schaub (litb) on
bf wrote:

> Unfortunately it fails if T isn't default constructible. A possible
> solution can be made by defaulting the template parameter, instead of
> an explicit default constructor.
>
> template <typename T>
> class wrapped
> {
> public:
> template <typename U = T>
> wrapped(U&& u = U()) : t(std::forward<U>(u) {}
> private:
> T t;
> };
>
> This allows default construction if T supports it, so logically it
> works. In my experiments with g++, however, I see that the defaulted
> value isn't optimized away. The move constructor for T is presumably
> cheap, so it's probably not the end of the world, but it's
> unnecessary. QOI detail, or behaviour mandated by proposed standard
> wording?
>
> I think a solution can be made with a variadic template constructor,
> which also nicely takes care of multi-parameter constructors, but I
> fail to figure out how to do a std::forward<U...>(u...). Suggestions?
> _

Notice that a simple variadic constructor has bad behavior for overload
resolution:

void f(wrapped<int>);
void f(string);

// eww, ambiguous!
int main() { f("foo"); }

For inheriting constructors, this problem is solved by saying "using
base::base;". For forwarding data member constructors, you can use SFINAE

template <typename T>
class wrapped
{
public:
template <typename ...U,
typename = bool[is_constructible<T, U...>::value]>
wrapped(U&& ...u) : t(std::forward<U>(u)...) {}

private:
T t;
};


This still isn't perfect, as the following example shows:

void f(wrapped< vector<string> >);
void f(wrapped<int>);

int main() { f(42); } // ambiguous!

The "wrapped" constructor template is non-explicit, thus being available
both times and not SFINAE'ed out because vector has an explicit ctor taking
an int. is_constructible tells us that it will work. Of course if you were
to use it unwrapped, you could call "f" fine. Not only because 42 -> int
would be an exact match, but because "42 -> vector<string>" wouldn't be
possible in the first place.

If you aim for equivalence to the non-wrapped case, you have to simulate
copy-initialization and direct initialization distinction (explicit and non-
explicit constructors). I don't know whether that's possible at all. But i
can't see a real pressing need right now.

--
[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]

From: SG on
On 16 Jul., 01:47, lucdantonwrote:
>
> Note also that under gcc-4.5 a variadic, perfect-forwarding
> constructor will mess copy construction two-fold:
>
> - a custom-defined constructor suppress the default instantiation of
> the copy constructor

No, that doesn't sound right. A constructor template is never
considered a copy constructor (see �12.8/2). If no copy constructor
has been declared a copy constructor is declared implicitly (see
�12.8/4).

Any user-defined constructor disables the implicit generation of a
default constructor. So, that's maybe what you were thinking of.

> This is obvious but if you forget it you might not notice it if the
> perfectly-forwarded call is valid construct!

I'm not following you.

> The obvious cure is adding the default copy constructor:

I don't see why this is supposed to be necessary.

> - the perfect-forwarding constructor eats reference to non-const.
> D'oh !
> So assuming you do have a copy constructor:
> some_type s;
> auto const& cr = s;
> some_type(cr); // fine!
> some_type(s); // Uh oh! some_type(U&& ...u) gets called with U =
> some_typ&!
>
> I don't know if this is intended by the standard or a quirk of gcc-4.5
> (rvalue refs 1.0 got redesigned for this kind of gotcha's I believe,
> but much worse).

As far as I can tell this follows from the overload resolution rules.

> I've had some luck disabling that sort of behaviour
> with std::enable_if and std::is_same (from <type_traits>) but I'm not
> that familiar with template metaprogramming so I don't think I've done
> it for _variadic_ perfect-forwarding constructors.

I can think of two approaches to handle this situation.

(1) Offer both copy constructors (one taking ref-to-const and another
taking a ref-to-non-const)

(2) Split the variadic contructor template into two templates: one
handling a single argument and another handling two or more
arguments. The first one would use SFINAE to disable the
U=some_type& case.


Cheers!
SG


--
[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]