Velocity Reviews - Computer Hardware Reviews

Velocity Reviews > Newsgroups > Programming > C++ > not1/not2 support in C++11

Reply
Thread Tools

not1/not2 support in C++11

 
 
Scott Meyers
Guest
Posts: n/a
 
      12-21-2011
Consider the following program, which was sent to me by a colleague last
summer along with a question as to whether not1 and not2 were supposed
to work with bind:

#include <algorithm>
#include <array>
#include <functional>
#include <iostream>
using namespace std;
using namespace std:laceholders;

template<class FIter, class Fun, class Pred>
void for_each_if(FIter beg, FIter end, Fun f, Pred pred) {
while (beg != end) {
if (pred(*beg))
f(*beg);
++beg;
}
}

void f(int n) {
cout << n << "\n";
}

int main() {
array<int,4> a = {1,2,3,4};
for_each_if(a.begin(), a.end(), f, not1(bind2nd(modulus<int>(),2)));
// uses bind2nd and compiles
for_each_if(a.begin(), a.end(), f, not1(bind(modulus<int>(),_1,2)));
// uses bind and doesn't compile
}

The problem is that std::bind doesn't declare the typedefs that
std::not1 expects, notably argument_type. 20.8/2 says that std::bind's
return type is unspecified, but 20.8.9.1.2/3 says that it's a call
wrapper, and 20.8.1/3-6 say that call wrappers are function objects, and
20.8/5 says function objects taking 1 or 2 arguments have to offer
typedefs like argument_type. All this leads me to believe that not1 and
not2 should work with objects returned from std::bind (and also to
std::function objects) as long as they have arity 1 or 2. Is this valid
reasoning?

Thanks,

Scott
 
Reply With Quote
 
 
 
 
Michael DOUBEZ
Guest
Posts: n/a
 
      12-23-2011
On 21 déc, 19:11, Scott Meyers <(E-Mail Removed)> wrote:
> Consider the following program, which was sent to me by a colleague last
> summer along with a question as to whether not1 and not2 were supposed
> to work with bind:
>
> #include <algorithm>
> #include <array>
> #include <functional>
> #include <iostream>
> using namespace std;
> using namespace std:laceholders;
>
> template<class FIter, class Fun, class Pred>
> void for_each_if(FIter beg, FIter end, Fun f, Pred pred) {
> * *while (beg != end) {
> * * *if (pred(*beg))
> * * * *f(*beg);
> * * *++beg;
> * *}
>
> }
>
> void f(int n) {
> * *cout << n << "\n";
>
> }
>
> int main() {
> * *array<int,4> a = {1,2,3,4};
> * *for_each_if(a.begin(), a.end(), f, not1(bind2nd(modulus<int>(),2)));
> * // uses bind2nd and compiles
> * *for_each_if(a.begin(), a.end(), f, not1(bind(modulus<int>(),_1,2)));
> * // uses bind and doesn't compile
>
> }
>
> The problem is that std::bind doesn't declare the typedefs that
> std::not1 expects, notably argument_type. *20.8/2 says that std::bind's
> return type is unspecified, but 20.8.9.1.2/3 says that it's a call
> wrapper, and 20.8.1/3-6 say that call wrappers are function objects, and
> 20.8/5 says function objects taking 1 or 2 arguments have to offer
> typedefs like argument_type. *All this leads me to believe that not1 and
> not2 should work with objects returned from std::bind (and also to
> std::function objects) as long as they have arity 1 or 2. *Is this valid
> reasoning?


See
http://drdobbs.com/184401949
[...]
Again, there are cases where TR1-style call wrapper types cannot
define a nested type that gives the return type of their function call
operators. Since Standard Library-style call wrappers depend on
result_type to determine the return type of their function call
operators, this sometimes means that you can't use a TR1-style call
wrapper type as the template argument to a Standard Library call
wrapper type. [...]

In your case, this may be a quality of implementation issue because
bind should be able to determine the argument/return types.

As for the wording of the standard, this may be a defect; IIRC the
return value of bind() is supposed to be unspecified.

--
Michael
 
Reply With Quote
 
 
 
 
Scott Meyers
Guest
Posts: n/a
 
      12-23-2011
On 12/23/2011 3:41 AM, Michael DOUBEZ wrote:
> See
> http://drdobbs.com/184401949
> [...]
> Again, there are cases where TR1-style call wrapper types cannot
> define a nested type that gives the return type of their function call
> operators. Since Standard Library-style call wrappers depend on
> result_type to determine the return type of their function call
> operators, this sometimes means that you can't use a TR1-style call
> wrapper type as the template argument to a Standard Library call
> wrapper type. [...]


That information applies only to TR1, which was dependent on C++03. I'm
asking about C++11, which includes new facilities, including decltype.
Given decltype, I believe it is always possible for the call wrapper
returned by std::bind to statically compute result_type.

> In your case, this may be a quality of implementation issue because
> bind should be able to determine the argument/return types.


It's not a QOI issue if the standard requires it, and the reasoning I
posted suggests to me that it is.

> As for the wording of the standard, this may be a defect; IIRC the
> return value of bind() is supposed to be unspecified.


The return type is unspecified, but it's defined to be a call wrapper,
and the standard imposes requirements on call wrappers. It's similar to
the situation with lambda expressions, where the type of the created
function object is unspecified, but there are still some guarantees
about it we can depend on.

Scott
 
Reply With Quote
 
Scott Meyers
Guest
Posts: n/a
 
      12-23-2011
On 12/23/2011 5:35 AM, Leigh Johnston wrote:
> Actually just because unary_function and binary_function are deprecated
> doesn't mean that the typedefs are also deprecated as the typedefs are
> now (in C++11) directly contained in the standard functors themselves
> (such as std::less) which are no longer derived from
> unary_function/binary_function.


Right. At least that's my understanding.

Scott
 
Reply With Quote
 
Howard Hinnant
Guest
Posts: n/a
 
      12-24-2011
On Dec 21, 1:11*pm, Scott Meyers <(E-Mail Removed)> wrote:
> Consider the following program, which was sent to me by a colleague last
> summer along with a question as to whether not1 and not2 were supposed
> to work with bind:
>
> #include <algorithm>
> #include <array>
> #include <functional>
> #include <iostream>
> using namespace std;
> using namespace std:laceholders;
>
> template<class FIter, class Fun, class Pred>
> void for_each_if(FIter beg, FIter end, Fun f, Pred pred) {
> * *while (beg != end) {
> * * *if (pred(*beg))
> * * * *f(*beg);
> * * *++beg;
> * *}
>
> }
>
> void f(int n) {
> * *cout << n << "\n";
>
> }
>
> int main() {
> * *array<int,4> a = {1,2,3,4};
> * *for_each_if(a.begin(), a.end(), f, not1(bind2nd(modulus<int>(),2)));
> * // uses bind2nd and compiles
> * *for_each_if(a.begin(), a.end(), f, not1(bind(modulus<int>(),_1,2)));
> * // uses bind and doesn't compile
>
> }
>
> The problem is that std::bind doesn't declare the typedefs that
> std::not1 expects, notably argument_type. *20.8/2 says that std::bind's
> return type is unspecified, but 20.8.9.1.2/3 says that it's a call
> wrapper, and 20.8.1/3-6 say that call wrappers are function objects, and
> 20.8/5 says function objects taking 1 or 2 arguments have to offer
> typedefs like argument_type. *All this leads me to believe that not1 and
> not2 should work with objects returned from std::bind (and also to
> std::function objects) as long as they have arity 1 or 2. *Is this valid
> reasoning?


I could be wrong here, bind is quite a complex machine, but I don't
think this is going to work for any known bind implementation. Nor do
I believe the committee intended that it work. The bind functor is
specified to have the typedef result_type, at least some of the time,
as bind is specified ([func.bind.bind]/p3) to have a weak result type
(which is subsequently specified in [func.require]/p3). But the
argument typedefs are more problematic.

Note that the functor returned by bind(modulus<int>(),_1,2) isn't just
a unary functor. It can be called with:

bind(modulus<int>(),_1,2)(3, 4) // equivalent to modulus<int>()
(3, 2)

making it act like a binary functor. So one client may believe that
bind(modulus<int>(),_1,2) should have argument_type, whereas another
client may be expecting first_argument_type and second_argument_type.
It is difficult for bind to figure out at compile time exactly what
the arguments will be at bind-object construction time.

boost::bind solves this problem by providing a operator!() on bind:

* *for_each_if(a.begin(), a.end(), f, !bind(modulus<int>(),_1,2));

This idea was considered and rejected as too late for C++11.

http://www.open-std.org/jtc1/sc22/wg...3224.html#gb97

Another workaround is (I believe) to use lambdas:

for_each_if(a.begin(), a.end(), f, [](int x){return !
bind(modulus<int>(),x,2)();});

boost's operator!() is a lot nicer (imho).

One could also write a generalized not functor fairly easily as in
this stack overflow question:

http://stackoverflow.com/q/5567342/576911

for_each_if(a.begin(), a.end(), f,
not_(bind(modulus<int>(),_1,2)));

which approaches the syntactic nicety of operator!() for bind.

Howard


 
Reply With Quote
 
Howard Hinnant
Guest
Posts: n/a
 
      12-25-2011
On Dec 24, 3:04*pm, Howard Hinnant <(E-Mail Removed)> wrote:
> I could be wrong here, bind is quite a complex machine, but I don't
> think this is going to work for any known bind implementation. *Nor do
> I believe the committee intended that it work.


I was thinking that maybe I could implement this, if I were just
clever enough. But I'm beginning to think it is unimplementable.
Consider this case:

#include <functional>
#include <cassert>

using namespace std;
using namespace std:laceholders;

long long
g(char a, int b, unsigned c)
{
return a + b + c;
}

int main()
{
auto p = bind(g, _2, _2, _2);
assert(p() == 9); // #1
assert(p(5.5) == 9); // #2
assert(p(5.5, 3ull) == 9); // #3
assert(p(5.5, 3ull, 'a') == 9); // #4
assert(p(5.5, 3ull, 'a', 6u) == 9); // #5
}

#1 and #2 do not compile. So let's not worry about those. But this
implies that that decltype(p) is not a unary_function, and thus
doesn't have an argument_type type.

#3 compiles. It is a binary_function. What is first_argument_type?
It could be anything! It is completely ignored. char is no more
appropriate than int, which is no more appropriate than class X! (you
can read that as an exclamation point or a factorial operator ).
second_argument_type must be convertible to all of char, int and
unsigned. But which, if any, of those three types should
second_argument_type be?

#4 and #5 also compile and work correctly. But they aren't unary or
binary functors, and yet the decltype(p) is the same type.

*Maybe* a sufficiently smart bind could not define first_argument_type
in this case, or maybe set it to void. And *maybe* it could set
second_argument_type to common_type<char, int, unsigned>::type. But
would this actually be useful? The rules get so complicated that it
becomes difficult for clients to have an obvious and expected outcome
of bind.

*Maybe* the design of argument_type, first_argument_type,
second_argument_type, not1 and not2 are simply obsolete with respect
to bind. boost::bind has moved beyond these needs/ideas for years
now.

Howard
 
Reply With Quote
 
Scott Meyers
Guest
Posts: n/a
 
      12-26-2011
On 12/24/2011 12:04 PM, Howard Hinnant wrote:
> One could also write a generalized not functor fairly easily as in
> this stack overflow question:
>
> http://stackoverflow.com/q/5567342/576911
>
> for_each_if(a.begin(), a.end(), f,
> not_(bind(modulus<int>(),_1,2)));
>
> which approaches the syntactic nicety of operator!() for bind.


I personally like this solution, because it solves the problem for more
than just the function objects that std::bind produces, but given that
Boost supports operator! for boost::bind-produced objects, adding that
requirement to std::bind-produced objects seems reasonable to me.

My current impression is that not1 and not2 should probably have been
deprecated along with bind1st and bind2nd.

I think that the standard's wording regarding the constraints on
std::bind-produced objects and the availability of argument- and
result-type typedefs should probably be revised, because I think that
the line of reasoning I originally posted is valid. Thanks to your very
thorough discussion of the implementation difficulties, however, it
seems apparent that the best solution is to make it clear that objects
produced by std::bind are never guaranteed to offer the typedefs that
not1/not2 require.

Regarding the implementation of class negation in the StackOverflow
article you referred to, I was surprised to see no use of std::forward
on the arguments to operator() when forwarding them. I think this may
be a common oversight for people writing forwarding functions: they
remember to write a template, they remember to use "&&" parameters, they
remember to use variadic templates, but they forget to apply
std::forward to the arguments they forward. I've seen it more than
once. Funny. Either that or I'm funny for thinking that they need to
use std::forward in such contexts....

Thanks for your very detailed posts, and thanks especially for taking
the time right before Christmas.

Scott


 
Reply With Quote
 
 
 
Reply

Thread Tools

Posting Rules
You may not post new threads
You may not post replies
You may not post attachments
You may not edit your posts

BB code is On
Smilies are On
[IMG] code is On
HTML code is Off
Trackbacks are On
Pingbacks are On
Refbacks are Off


Similar Threads
Thread Thread Starter Forum Replies Last Post
IEEE 1284.4 generic printing support / HP LaserJet printer support Manuel Lopez Windows 64bit 13 10-18-2009 12:51 PM
Question for IT Support/Managers - Providing Personal PC Support Big Dog Microsoft Certification 4 01-16-2008 01:33 AM
Anti Virus Support + Japanese character support =?Utf-8?B?Q2hyaXMgQnVzaA==?= Windows 64bit 6 07-31-2005 09:57 AM
ANN: SCons.0.96 adds Fortran 90/95 support, better Qt support,platform-independent file system actions, improved debugging, lots more Steven Knight Python 0 08-18-2004 03:57 PM
Getting Third Party Component Suppliers to support NUnit and NUnitASP to support test driven development in web pages Nick Zdunic ASP .Net 0 11-05-2003 10:45 AM



Advertisments