Topic: Should we have a core language operator for forwarding arguments?
Author: Nikolay Ivchenkov <tsoae@mail.ru>
Date: Wed, 9 Nov 2011 19:10:07 CST
Raw View
Consider the following example:
template <class T>
void f(T &&t)
{
g(std::forward<T>(t));
}
We can notice that parameter t contains sufficient information for
"perfect forwarding":
#define FWD(x) static_cast<decltype(x)>(x)
template <class T>
void f(T &&t)
{
g(FWD(t));
}
This code might be even simpler if we had unary operator && with the
similar semantics:
&&reference returns static_cast<decltype(reference)>(reference)
Example:
template <class T>
void f(T &&t)
{
g(&&t);
}
I think that simple things should be expressed by simple constructs.
std::forward should be used where its function argument would not
contain sufficient information, otherwise we supply redundant
information and have too lengthy code (especially when std::forward is
included in a single expression several times).
--
[ comp.std.c++ is moderated. To submit articles, try posting with your ]
[ newsreader. If that fails, use mailto:std-cpp-submit@vandevoorde.com ]
[ --- Please see the FAQ before posting. --- ]
[ FAQ: http://www.comeaucomputing.com/csc/faq.html ]
Author: =?ISO-8859-1?Q?Daniel_Kr=FCgler?=<daniel.kruegler@googlemail.com>
Date: Thu, 10 Nov 2011 07:58:39 -0800 (PST)
Raw View
On 2011-11-10 02:10, Nikolay Ivchenkov wrote:
>
> Consider the following example:
>
> template<class T>
> void f(T&&t)
> {
> g(std::forward<T>(t));
> }
>
> We can notice that parameter t contains sufficient information for
> "perfect forwarding":
>
> #define FWD(x) static_cast<decltype(x)>(x)
>
> template<class T>
> void f(T&&t)
> {
> g(FWD(t));
> }
>
> This code might be even simpler if we had unary operator&& with the
> similar semantics:
>
> &&reference returns static_cast<decltype(reference)>(reference)
>
> Example:
>
> template<class T>
> void f(T&&t)
> {
> g(&&t);
> }
>
> I think that simple things should be expressed by simple constructs.
> std::forward should be used where its function argument would not
> contain sufficient information, otherwise we supply redundant
> information and have too lengthy code (especially when std::forward is
> included in a single expression several times).
I agree, I find your example very much compelling. There are some cases,
where I still prefer using std::forward, namely where I could
(unintentionally) have tried to convert an rvalue to an lvalue as
analysed in
http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2009/n2951.html
This does not happen in your examples, though.
Thanks& Greetings from Bremen,
Daniel Kr gler
--
[ comp.std.c++ is moderated. To submit articles, try posting with your ]
[ newsreader. If that fails, use mailto:std-cpp-submit@vandevoorde.com ]
[ --- Please see the FAQ before posting. --- ]
[ FAQ: http://www.comeaucomputing.com/csc/faq.html ]
Author: Arne Mertz<arnemertz@googlemail.com>
Date: Thu, 10 Nov 2011 10:41:39 -0800 (PST)
Raw View
On Nov 10, 2:10 am, Nikolay Ivchenkov<ts...@mail.ru> wrote:
> I think that simple things should be expressed by simple constructs.
> std::forward should be used where its function argument would not
> contain sufficient information, otherwise we supply redundant
> information and have too lengthy code (especially when std::forward is
> included in a single expression several times).
Short answer: std::forward is sufficient. No need to tweak the
language syntax.
Long answer: Normally, features will not be forced into the language,
if they can easily be implemented in the library in a sufficient short
and compact way.
In addition, introducing your proposed unary operator for many but not
all applications of std::forward would not get us rid of that library
feature.
All together it would save us some typing but on the other hand
introduce some confusion ("Hey, why did he use std::forward instead of
the forwarding operator?").
There are more arguments NOT to use something like an unary op&&, e.g.
searchability (try grepping your code for&& instead of std::forward).
By the way, why stop at std::forward? What about std::move? Why not
introduce some move-Operator as well?
At the end we'd end up in some Perl'ish mess of operator rubbish and
four different ways to express every little language feature. Let's
not even start to go there ;)
Regards
Arne
--
[ comp.std.c++ is moderated. To submit articles, try posting with your ]
[ newsreader. If that fails, use mailto:std-cpp-submit@vandevoorde.com ]
[ --- Please see the FAQ before posting. --- ]
[ FAQ: http://www.comeaucomputing.com/csc/faq.html ]
Author: Nikolay Ivchenkov<tsoae@mail.ru>
Date: Fri, 11 Nov 2011 13:04:36 -0800 (PST)
Raw View
On 10 Nov, 22:41, Arne Mertz wrote:
> On Nov 10, 2:10 am, Nikolay Ivchenkov<ts...@mail.ru> wrote:
>
> > I think that simple things should be expressed by simple constructs.
> > std::forward should be used where its function argument would not
> > contain sufficient information, otherwise we supply redundant
> > information and have too lengthy code (especially when std::forward is
> > included in a single expression several times).
>
> Short answer: std::forward is sufficient. No need to tweak the
> language syntax.
> Long answer: Normally, features will not be forced into the language,
> if they can easily be implemented in the library in a sufficient short
> and compact way.
Currently we can easily implement forwarding without any library
components:
template<class T>
void f(T&&t)
{ g(static_cast<T&&>(t)); }
And in fact, std::forward (where it is applicable) does not have
significant advantages over plain static_cast operator.
> In addition, introducing your proposed unary operator for many but not
> all applications of std::forward would not get us rid of that library
> feature.
That's not a problem. Personally, I don't want to use an awkward
construct in some particular case just because it solves more general
issue. It's normal to have different facilities for expressing the
same thing.
Examples:
1.1)
n = n + 1;
1.2)
n += 1;
1.3)
++n;
2.1)
for (; condition; )
2.2)
while (condition)
> All together it would save us some typing but on the other hand
> introduce some confusion ("Hey, why did he use std::forward instead of
> the forwarding operator?").
Good question. The forwarding operator can be more safe than
std::forward. Consider the following example:
template<class T>
void f(T t)
{ g(std::forward<T>(t)); }
Note: the parameter t is declared as having type T, not T&&. Did the
author forget about&&? If so, how can std::forward help us to detect
such an error? Unlike a function template, a macro is able to
determine whether its agrument is a reference (see below) and a built-
in operator is able to "know" even more about its operand (e.g.
whether the operand is a named reference).
#include<type_traits>
#define FWD(ref) \
static_cast<typename ref_checker<decltype(ref)>::type>(ref)
template<class T>
struct ref_checker
{
static_assert(
std::is_reference<T>(),
"The argument of FWD shall be declared as reference");
typedef T type;
};
#include<iostream>
#include<utility>
template<class T>
void f(T t)
{ g(FWD(t)); }
struct X {};
void g(X const&)
{
std::cout<< "copy\n";
}
void g(X&&)
{
std::cout<< "move\n";
}
int main()
{
X x;
f(x);
f(X());
}
> There are more arguments NOT to use something like an unary op&&, e.g.
> searchability (try grepping your code for&& instead of std::forward).
I know only one useful application of such an ability for
std::forward: it's easy to replace this awkward thing with more
suitable construct (e.g. with the macro that was described above) :-)
> By the way, why stop at std::forward? What about std::move? Why not
> introduce some move-Operator as well?
There is a very limited set of potential readable short operators that
can be introduced into the language without breakage of the existing
grammar. It would be fine to have built-in operator for moving as
well, however, I can't imagine widespread use of such a feature in
parts of code where brevity is critical.
I hope that the next standard will allow to use local templates and
polymorphic lambdas. Lambda-expressions that are used as
subexpressions look fine only when they are small. That's why syntax
of lambdas (including hypothetical polymorphic ones) should be as
brief as possible. A code with a polymorphic lambda might look as
follows:
template<class T>
void f(T&&t)
{
apply_visitor(&&t, [](auto&&src) { g(&&src); });
}
which would be equivalent to
template<class T>
void f(T&&t)
{
struct Closure
{
template<class Auto>
void operator ()(Auto&&src) const
{ g(static_cast<decltype(src)>(src)); };
};
apply_visitor(static_cast<decltype(t)>(t), Closure());
}
--
[ comp.std.c++ is moderated. To submit articles, try posting with your ]
[ newsreader. If that fails, use mailto:std-cpp-submit@vandevoorde.com ]
[ --- Please see the FAQ before posting. --- ]
[ FAQ: http://www.comeaucomputing.com/csc/faq.html ]
Author: Miles Bader <miles@gnu.org>
Date: Sat, 12 Nov 2011 11:33:08 -0800 (PST)
Raw View
Nikolay Ivchenkov<tsoae@mail.ru> writes:
> Currently we can easily implement forwarding without any library
> components:
>
> template<class T>
> void f(T&&t)
> { g(static_cast<T&&>(t)); }
>
> And in fact, std::forward (where it is applicable) does not have
> significant advantages over plain static_cast operator.
Sure it does: it expresses intent, which makes user code easier to
understand.
Names are a good thing. :)
Sure the extra verbosity can be annoying or even make code less
readable for operations which are very common (e.g. "+" vs. "plus"),
but I find it hard to believe std::forward falls into that category...
-Miles
--
"Whatever you do will be insignificant, but it is very important that
you do it." Mahatma Gandhi
[ comp.std.c++ is moderated. To submit articles, try posting with your ]
[ newsreader. If that fails, use mailto:std-cpp-submit@vandevoorde.com ]
[ --- Please see the FAQ before posting. --- ]
[ FAQ: http://www.comeaucomputing.com/csc/faq.html ]
Author: Nikolay Ivchenkov<tsoae@mail.ru>
Date: Sat, 12 Nov 2011 21:46:16 -0800 (PST)
Raw View
On 12 Nov, 23:33, Miles Bader wrote:
> Nikolay Ivchenkov<ts...@mail.ru> writes:
> > And in fact, std::forward (where it is applicable) does not have
> > significant advantages over plain static_cast operator.
>
> Sure it does: it expresses intent, which makes user code easier to
> understand.
That's why I didn't say that it doesn't have advantages over
static_cast at all :-) However, I don't consider such advantage as
significant. We don't need to include<utility> for static_cast, but
this is not a significant advantage of static_cast over std::forward
as well.
> Sure the extra verbosity can be annoying or even make code less
> readable for operations which are very common (e.g. "+" vs. "plus"),
> but I find it hard to believe std::forward falls into that category...
I have some experience with std::forward and I wouldn't suggest a
replacement for this thing if its verbosity wouldn't be annoying for
me :-)
--
[ comp.std.c++ is moderated. To submit articles, try posting with your ]
[ newsreader. If that fails, use mailto:std-cpp-submit@vandevoorde.com ]
[ --- Please see the FAQ before posting. --- ]
[ FAQ: http://www.comeaucomputing.com/csc/faq.html ]