[type_traits] has_equal_to< std::vector<T> > always true_

-- View this message in context: http://boost.2283326.n4.nabble.com/boost-type-traits-has-equal-to-std-vector... Sent from the Boost - Dev mailing list archive at Nabble.com.

On Thu, Oct 20, 2011 at 7:55 AM, lcaminiti <lorcaminiti@gmail.com> wrote:
Hello all,
has_equal_to< std::vector<T> > always returns true_ because == is defined for std::vector<T> even when it is not defined for T. However, then an attempt to compare for equality std::vector<T> will generate a compiler error in this case because there is no == for T.
For example:
#include <boost/type_traits/has_equal_to.hpp> #include <vector> #include <iostream>
struct myvalue {};
//bool operator== ( myvalue const& l, myvalue const& r ) { return false; } // (1)
int main ( ) { typedef std::vector<myvalue> myvector; std::cout << boost::has_equal_to<myvector>::value << std::endl; // always returns true myvector v(1), w(1); std::cout << (v == w) << std::endl; // compiler error because (1) is not defined return 0; }
Shall has_equal_to< std::vector<T> > be smarter and return false_ if T does not have == ? (If not, this case should at least be documented.)
I'd venture to say this is a deficiency in std::vector's implementation of operator==, which ideally should SFINAE-out bindings of T which do not have an operator==. You get the same sort of problem if you have a class with an unbounded conversion constructor template: struct X { template< class T > X(T x) { /*...*/ } }; Here, I believe, boost::is_convertible< Y, X > will evaluate to true, and some bindings of Y to T may result in a compiler error (depending on what's in /*...*/). I *seem* to remember this was a problem with boost::variant, for example, but even if it wasn't, you can imagine how it could be (the template parameter in the constructor should only be bindable to one of the value_types of the variant). In my opinion, the upshot is that std::vector's operator== is almost certainly not a special case, so I don't know know if it's worth specializing has_equal_to on it or not. Maybe, if it's consistently done for all problematic std-defined operators? P.S. Are the strange HTML tags for < and > gone?
Yes, but the above quoted message only appeared when I clicked on the link: http://boost.2283326.n4.nabble.com/boost-type-traits-has-equal-to-lt-std-vec...

Le 20/10/11 17:09, Jeffrey Lee Hellrung, Jr. a écrit :
On Thu, Oct 20, 2011 at 7:55 AM, lcaminiti<lorcaminiti@gmail.com> wrote:
Hello all,
has_equal_to< std::vector<T> > always returns true_ because == is defined for std::vector<T> even when it is not defined for T. However, then an attempt to compare for equality std::vector<T> will generate a compiler error in this case because there is no == for T.
<snip>
Shall has_equal_to< std::vector<T> > be smarter and return false_ if T does not have == ? (If not, this case should at least be documented.)
I'd venture to say this is a deficiency in std::vector's implementation of operator==, which ideally should SFINAE-out bindings of T which do not have an operator==. <snip>
In my opinion, the upshot is that std::vector's operator== is almost certainly not a special case, so I don't know know if it's worth specializing has_equal_to on it or not. Maybe, if it's consistently done for all problematic std-defined operators?
Hi, I found the same kind of issues while implementing is_constructible/is_assignable/is_convertible for Boost.Conversion. The option I taken was to specialize these traits for the c++ implementations that were not using SFINAE. This force of course the user to include the specific file containing the specialization. BTW Ion, it seems that your implementation on Boost.Container is not using SFINAE neither. Please, could you add them to conform to the C++11 specification? Best, Vicente

Vicente Botet wrote:
Le 20/10/11 17:09, Jeffrey Lee Hellrung, Jr. a écrit :
On Thu, Oct 20, 2011 at 7:55 AM, lcaminiti<lorcaminiti@> wrote:
Hello all,
has_equal_to< std::vector<T> > always returns true_ because == is defined for std::vector<T> even when it is not defined for T. However, then an attempt to compare for equality std::vector<T> will generate a compiler error in this case because there is no == for T.
<snip>
Shall has_equal_to< std::vector<T> > be smarter and return false_ if T does not have == ? (If not, this case should at least be documented.)
I'd venture to say this is a deficiency in std::vector's implementation of operator==, which ideally should SFINAE-out bindings of T which do not have an operator==. <snip>
In my opinion, the upshot is that std::vector's operator== is almost certainly not a special case, so I don't know know if it's worth specializing has_equal_to on it or not. Maybe, if it's consistently done for all problematic std-defined operators?
Hi,
I found the same kind of issues while implementing is_constructible/is_assignable/is_convertible for Boost.Conversion. The option I taken was to specialize these traits for the c++ implementations that were not using SFINAE. This force of course the user to include the specific file containing the specialization.
Would the Boost.TypeTraits authors consider adding the specializations* to workaround STL not using SFINAE? If not, can these cases be documented? (*) In this case it'd be nice to name the headers containing the extra specialization consistently with other Boost libraries like Boost.Conversion-- Vicente, how did you name the headers containing the specialization for the STL types? Thanks. --Lorenzo -- View this message in context: http://boost.2283326.n4.nabble.com/boost-type-traits-has-equal-to-std-vector... Sent from the Boost - Dev mailing list archive at Nabble.com.

Le 21/10/11 17:38, lcaminiti a écrit :
Vicente Botet wrote:
Hi,
I found the same kind of issues while implementing is_constructible/is_assignable/is_convertible for Boost.Conversion. The option I taken was to specialize these traits for the c++ implementations that were not using SFINAE. This force of course the user to include the specific file containing the specialization.
Would the Boost.TypeTraits authors consider adding the specializations* to workaround STL not using SFINAE? If not, can these cases be documented?
(*) In this case it'd be nice to name the headers containing the extra specialization consistently with other Boost libraries like Boost.Conversion-- Vicente, how did you name the headers containing the specialization for the STL types?
I added all of them in a std directory with the same name as in the standard. For example boost/conversion/std/utility.hpp included some specializations for std::pair, boost/conversion/std/vector.hpp included some specializations for std::vector, ... In this way the user just needs to replace #include <utility> by #include <boost/conversion/std/utility.hpp> If several libraries use the same convention, it will be quite easy to provide some files that add some specificities to the standard files in for example the directory std boost/std/utility.hpp could include #include <boost/type_traits/std/utility.hpp> #include <boost/serialization/std/utility.hpp> #include <boost/XXXX/std/utility.hpp> .... Best, Vicente

has_equal_to< std::vector<T> > always returns true Would the Boost.TypeTraits authors consider adding the specializations* to workaround STL not using SFINAE? If not, can these cases be documented?
Hi, Thanks for highlighting this major issue. I understand that there is nothing to do apart from specializing the traits for those particular std classes. This would however not work with user classes. As the beta is due on Monday, I believe it is too short to submit a working and tested patch. Maybe once the beta is out, you can start to work on a proposed contribution? Meanwhile, as suggested I will add a warning in the doc.
boost/conversion/std/vector.hpp included some specializations for std::vector,
This seems a reasonable naming convention. Regards, Frédéric

on Fri Oct 21 2011, Frédéric Bron <frederic.bron-AT-m4x.org> wrote:
has_equal_to< std::vector<T> > always returns true Would the Boost.TypeTraits authors consider adding the specializations* to workaround STL not using SFINAE? If not, can these cases be documented?
Hi,
Thanks for highlighting this major issue. I understand that there is nothing to do apart from specializing the traits for those particular std classes. This would however not work with user classes. As the beta is due on Monday, I believe it is too short to submit a working and tested patch. Maybe once the beta is out, you can start to work on a proposed contribution?
I'm not sure, but it's *conceivable* that with C++11 extended SFINAE it might be possible to produce a has_equal_to that fails if the operator can't be instantiated. Somebody else around here surely knows better than I do. -- Dave Abrahams BoostPro Computing http://www.boostpro.com

Le 22/10/11 01:13, Dave Abrahams a écrit :
on Fri Oct 21 2011, Frédéric Bron<frederic.bron-AT-m4x.org> wrote:
has_equal_to< std::vector<T> > always returns true Would the Boost.TypeTraits authors consider adding the specializations* to workaround STL not using SFINAE? If not, can these cases be documented? Hi,
Thanks for highlighting this major issue. I understand that there is nothing to do apart from specializing the traits for those particular std classes. This would however not work with user classes. As the beta is due on Monday, I believe it is too short to submit a working and tested patch. Maybe once the beta is out, you can start to work on a proposed contribution? I'm not sure, but it's *conceivable* that with C++11 extended SFINAE it might be possible to produce a has_equal_to that fails if the operator can't be instantiated. Somebody else around here surely knows better than I do.
I think so. The problem appears with compilers that don't supports C++11 extended SFINAE and those standard library doesn't protect the operator instantiation. Vicente

Thanks for highlighting this major issue. I understand that there is nothing to do apart from specializing the traits for those particular std classes. This would however not work with user classes. As the beta is due on Monday, I believe it is too short to submit a working and tested patch. Maybe once the beta is out, you can start to work on a proposed contribution? Meanwhile, as suggested I will add a warning in the doc.
I have committed to trunk (r75084) and branches/release (r75085) a documentation patch that explains the issue with an example. Regards, Frédéric

Thanks for highlighting this major issue. I understand that there is nothing to do apart from specializing the traits for those particular std classes. This would however not work with user classes. As the beta is due on Monday, I believe it is too short to submit a working and tested patch. Maybe once the beta is out, you can start to work on a proposed contribution? Meanwhile, as suggested I will add a warning in the doc.
It's a tricky one, because strictly speaking vector<T> always does have an equality operator - it's just that depending upon T it may not compile :-( There's no general solution to this, because there's no way in the language to test whether something does or does not compile - we can only test whether it's declared or not. John.

Le 22/10/11 10:25, John Maddock a écrit :
Thanks for highlighting this major issue. I understand that there is nothing to do apart from specializing the traits for those particular std classes. This would however not work with user classes. As the beta is due on Monday, I believe it is too short to submit a working and tested patch. Maybe once the beta is out, you can start to work on a proposed contribution? Meanwhile, as suggested I will add a warning in the doc.
It's a tricky one, because strictly speaking vector<T> always does have an equality operator - it's just that depending upon T it may not compile :-(
There's no general solution to this, because there's no way in the language to test whether something does or does not compile - we can only test whether it's declared or not.
Hi, I'm not sure I understand the standard correctly. The standard says container requirements "Requires: T is EqualityComparable". Does this means that the operator== must not participate in overload resolution when the requirements are not meet, or that the compilation will fail? IMO, it is the first. In other parts of the standard the wording is more explicit. For example in the chrono part, every function that has some constraints in the template types, state something like: "Remarks: This operator shall not participate in overload resolution unless Rep2 is implicitly convertible to CR(Rep1, Rep2)." The same kind of wording is found for std::pair: " template<class U, class V> pair(const pair<U, V>& p); Requires: is_constructible<first_type, const U&>::value is true and is_constructible<second_- type, const V&>::value is true. Effects: Initializes members from the corresponding members of the argument. Remark: This constructor shall not participate in overload resolution unless const U& is implicitly convertible to first_type and const V& is implicitly convertible to second_type. " I don't know if the standard is not enough precise respect to the operator== for container, but this merits clarification anyway. Best, Vicente

El 22/10/2011 12:18, Vicente J. Botet Escriba escribió:
I'm not sure I understand the standard correctly. The standard says container requirements "Requires: T is EqualityComparable".
Does this means that the operator== must not participate in overload resolution when the requirements are not meet, or that the compilation will fail? IMO, it is the first. In other parts of the standard the wording is more explicit. For example in the chrono part, every function that has some constraints in the template types, state something like:
I don't know if the first option is the correct, and I don't know if is_default_constructible<std::vector<T>> should be false because T might not be MoveConstructible. If it is not guaranteed by the standard, I don't think we should offer it. Making non-templated functions templated to use SFINAE could not provoke some unwanted ambiguities? Ion

On Oct 22, 2011, at 1:53 PM, Ion Gaztañaga wrote:
I don't know if is_default_constructible<std::vector<T>> should be false because T might not be MoveConstructible.
std::vector<T> is always default constructible because this constructor places no requirements on T. Howard

El 22/10/2011 22:32, Howard Hinnant escribió:
On Oct 22, 2011, at 1:53 PM, Ion Gaztañaga wrote:
I don't know if is_default_constructible<std::vector<T>> should be false because T might not be MoveConstructible.
std::vector<T> is always default constructible because this constructor places no requirements on T.
thanks Howard, I missed that now vector can have just EmplaceInsertable or just DefaultConstructible objects. best, Ion

On Oct 22, 2011, at 6:18 AM, Vicente J. Botet Escriba wrote:
I'm not sure I understand the standard correctly. The standard says container requirements "Requires: T is EqualityComparable".
Does this means that the operator== must not participate in overload resolution when the requirements are not meet, or that the compilation will fail? IMO, it is the first. In other parts of the standard the wording is more explicit. For example in the chrono part, every function that has some constraints in the template types, state something like:
"Remarks: This operator shall not participate in overload resolution unless Rep2 is implicitly convertible to CR(Rep1, Rep2)."
The LWG did not intend that template functions be SFINAE'd unless it has a remark about not participating in overload resolution. And such treatment was reserved for cases that required such treatment to avoid ambiguities in overload resolution, templated constructors so that the std::traits would behave correctly (as has been noted in this thread), or (in the case of chrono) to control conversions to desired subsets (e.g. exactly convertible). Newer parts of the standard were more likely to be treated in this way than older parts, with a notable exception being the "do the right thing clause" for the containers: [sequence.reqmts]/p14-15. We did not add a "SFINAE" requirement to operator== for vector because we did not have a has_equal_to trait. Such a feature certainly seems plausible for a future standard, though it is always difficult to predict what will and won't pass through committee. Howard

2011/10/21 Frédéric Bron <frederic.bron@m4x.org>:
has_equal_to< std::vector<T> > always returns true Would the Boost.TypeTraits authors consider adding the specializations* to workaround STL not using SFINAE? If not, can these cases be documented?
Hi,
Thanks for highlighting this major issue. I understand that there is nothing to do apart from specializing the traits for those particular std classes. This would however not work with user classes. As the beta is due on Monday, I believe it is too short to submit a working and tested patch. Maybe once the beta is out, you can start to work on a proposed contribution? Meanwhile, as suggested I will add a warning in the doc.
Makes sense.
boost/conversion/std/vector.hpp included some specializations for std::vector,
This seems a reasonable naming convention.
I agree (in fact, if I ever decide it's worth to provide a contracted version of the STL, it'll be in boost/contract/std/vector.hpp, etc). --Lorenzo

On Fri, Oct 21, 2011 at 5:02 PM, Frédéric Bron <frederic.bron@m4x.org> wrote:
has_equal_to< std::vector<T> > always returns true Would the Boost.TypeTraits authors consider adding the specializations* to workaround STL not using SFINAE? If not, can these cases be documented?
Hi,
Thanks for highlighting this major issue. I understand that there is nothing to do apart from specializing the traits for those particular std classes. This would however not work with user classes. As the beta is due on Monday, I believe it is too short to submit a working and tested patch. Maybe once the beta is out, you can start to work on a proposed contribution? Meanwhile, as suggested I will add a warning in the doc.
boost/conversion/std/vector.hpp included some specializations for std::vector,
This seems a reasonable naming convention.
Were these traits specialized for the STL in any of the latest Boost releases? Thanks. --Lorenzo

Frédéric Bron wrote
Were these traits specialized for the STL in any of the latest Boost releases?
don't think so.
Do I have permission to add the specializations for STL containers to trunk and then the to release 1.50? 1) I'd add something like this for each STL container that defines an operator==: template< typename T, class Alloc = std::allocator<T> > struct has_equal_to< std::vector<T, Alloc> > : has_equal_to<T> {}; 2) I'd add similar specializations for all traits that apply !=, <=, etc. 3) I'd add the specializations in the trait header type_traits/has_equal_to.hpp, etc and not into special headers type_traits/std/vector.hpp, .etc (because when I use has_equal_to on std::vector I'd always expect it to work according to the specialized version as a user and achieving expected behavior should require no extra user step like including type_traits/std/vector.hpp). I'm happy to change the code, check the regression tests, and I don't think any documentation needs to be changed (because the specializations just make the traits work as the user expects them to work). What do you think? Thanks. --Lorenzo -- View this message in context: http://boost.2283326.n4.nabble.com/boost-type-traits-has-equal-to-std-vector... Sent from the Boost - Dev mailing list archive at Nabble.com.

Le 25/04/12 15:02, lcaminiti a écrit :
Frédéric Bron wrote
Were these traits specialized for the STL in any of the latest Boost releases? don't think so.
Do I have permission to add the specializations for STL containers to trunk and then the to release 1.50?
1) I'd add something like this for each STL container that defines an operator==:
template< typename T, class Alloc = std::allocator<T> > struct has_equal_to< std::vector<T, Alloc> > : has_equal_to<T> {};
2) I'd add similar specializations for all traits that apply !=,<=, etc.
3) I'd add the specializations in the trait header type_traits/has_equal_to.hpp, etc and not into special headers type_traits/std/vector.hpp, .etc (because when I use has_equal_to on std::vector I'd always expect it to work according to the specialized version as a user and achieving expected behavior should require no extra user step like including type_traits/std/vector.hpp).
I'm happy to change the code, check the regression tests, and I don't think any documentation needs to be changed (because the specializations just make the traits work as the user expects them to work).
What do you think?
Why do you need to specialize this trait for STL containers? Do you mean that has_equal_to doesn't work for them? if no why? Sorry if this is something obvious. Best, Vicente

On Wed, Apr 25, 2012 at 10:28 AM, Vicente J. Botet Escriba < vicente.botet@wanadoo.fr> wrote:
Le 25/04/12 15:02, lcaminiti a écrit :
Frédéric Bron wrote
Were these traits specialized for the STL in any of the latest Boost
releases?
don't think so.
Do I have permission to add the specializations for STL containers to trunk and then the to release 1.50?
1) I'd add something like this for each STL container that defines an operator==:
template< typename T, class Alloc = std::allocator<T> > struct has_equal_to< std::vector<T, Alloc> > : has_equal_to<T> {};
2) I'd add similar specializations for all traits that apply !=,<=, etc.
3) I'd add the specializations in the trait header type_traits/has_equal_to.hpp, etc and not into special headers type_traits/std/vector.hpp, .etc (because when I use has_equal_to on std::vector I'd always expect it to work according to the specialized version as a user and achieving expected behavior should require no extra user step like including type_traits/std/vector.hpp).
I'm happy to change the code, check the regression tests, and I don't think any documentation needs to be changed (because the specializations just make the traits work as the user expects them to work).
What do you think?
Why do you need to specialize this trait for STL containers? Do you mean that has_equal_to doesn't work for them? if no why?
Sorry if this is something obvious.
As far as I've gathered, typical STL implementations won't "guard" the declaration of vector's operator== with something like Boost.EnableIf, so vector<X> is always reported as having an operator== regardless of what X is. Lorenzo: I would be slightly more in favor of keeping the std:: specializations for has_* in a separate header, similar to how Boost.Fusion's adaptation framework for, e.g., std::pair is in a separate header. I sympathize with your reasoning though so I could go either way. - Jeff

Le 25/04/12 20:17, Jeffrey Lee Hellrung, Jr. a écrit :
On Wed, Apr 25, 2012 at 10:28 AM, Vicente J. Botet Escriba< vicente.botet@wanadoo.fr> wrote:
Why do you need to specialize this trait for STL containers? Do you mean that has_equal_to doesn't work for them? if no why?
Sorry if this is something obvious.
As far as I've gathered, typical STL implementations won't "guard" the declaration of vector's operator== with something like Boost.EnableIf, so vector<X> is always reported as having an operator== regardless of what X is. Oh, I see now. I had the same issue while implementing is_assignable ;-)
Thanks, Vicente

Vicente Botet wrote
Le 25/04/12 20:17, Jeffrey Lee Hellrung, Jr. a écrit :
On Wed, Apr 25, 2012 at 10:28 AM, Vicente J. Botet Escriba< vicente.botet@> wrote:
Why do you need to specialize this trait for STL containers? Do you mean that has_equal_to doesn't work for them? if no why?
Sorry if this is something obvious.
As far as I've gathered, typical STL implementations won't "guard" the declaration of vector's operator== with something like Boost.EnableIf, so vector<X> is always reported as having an operator== regardless of what X is. Oh, I see now. I had the same issue while implementing is_assignable ;-)
Is is_assignable part of some Boost library public API? Did you put the specializations in a separate STL header or not? Thanks. --Lorenzo -- View this message in context: http://boost.2283326.n4.nabble.com/boost-type-traits-has-equal-to-std-vector... Sent from the Boost - Dev mailing list archive at Nabble.com.

lcaminiti wrote
Vicente Botet wrote
Le 25/04/12 20:17, Jeffrey Lee Hellrung, Jr. a écrit :
On Wed, Apr 25, 2012 at 10:28 AM, Vicente J. Botet Escriba< vicente.botet@> wrote:
Why do you need to specialize this trait for STL containers? Do you mean that has_equal_to doesn't work for them? if no why?
Sorry if this is something obvious.
As far as I've gathered, typical STL implementations won't "guard" the declaration of vector's operator== with something like Boost.EnableIf, so vector<X> is always reported as having an operator== regardless of what X is. Oh, I see now. I had the same issue while implementing is_assignable ;-)
Is is_assignable part of some Boost library public API? Did you put the specializations in a separate STL header or not?
No, these traits are included in the reviewed Boost.Conversion. Even if the review is pending I don't expect it to be accepted. I opted to have a file by std file containing specialization. http://svn.boost.org/svn/boost/sandbox/conversion/boost/conversion/std/ Others are also needed for Boost http://svn.boost.org/svn/boost/sandbox/conversion/boost/conversion/boost HTH, Vicente -- View this message in context: http://boost.2283326.n4.nabble.com/boost-type-traits-has-equal-to-std-vector... Sent from the Boost - Dev mailing list archive at Nabble.com.

Jeffrey Lee Hellrung, Jr.-2 wrote
Lorenzo: I would be slightly more in favor of keeping the std:: specializations for has_* in a separate header, similar to how Boost.Fusion's adaptation framework for, e.g., std::pair is in a separate header. I sympathize with your reasoning though so I could go either way.
I can also go either way. The argument for the separate header is that if a user has a STL implementation of operator== for std::vector that uses enable_if or alike than they can chose not to include the header given they don't need the specializations (but the specializations will work also with the smarter operator== implementations and if they are in the same has_equal_to header then the user never has to do "extra" work to get the trait to behave correctly). At the end, I'm happy to do what the Boost.TypeTraits authors reccommand: separate header or not. Thanks. --Lorenzo -- View this message in context: http://boost.2283326.n4.nabble.com/boost-type-traits-has-equal-to-std-vector... Sent from the Boost - Dev mailing list archive at Nabble.com.

Hi all,
At the end, I'm happy to do what the Boost.TypeTraits authors reccommand: separate header or not.
I am the author of the operator traits. I thought a bit about your proposals. I see pro and cons for both solutions. If std container specializations are: 1. in actual headers: a) easy to use (no need to add #include) b) forces user of has_equal_to to include <list>, <vector>, <deque>, <map>, <multi_map>, <set>, <multiset>... 2. in separate headers a) must #include an additionnal header, can have a header for each container and a header that includes all headers so that it is easy to include just the minimum b) no need to include unneeded headers I would favor solution 2 because I think 1b is too much overload. Another solution could be to have a macro that say if we must include the std specialization or not? John, you are the guru of Boost.TypeTraits, any comment? Lorenzo, could you look at all standard containers in detail and list all operators that are concerned for each of them? Frédéric

Hi Lorenzo,
1) I'd add something like this for each STL container that defines an operator==:
template< typename T, class Alloc = std::allocator<T> > struct has_equal_to< std::vector<T, Alloc> > : has_equal_to<T> {};
Halas, it is a bit more complicated because the operator traits have in fact 3 arguments: template <class Lhs, class Rhs=Lhs, class Ret=dont_care> struct has_equal_to : public true_type-or-false_type {}; So we have to deal with left hand side and right hand side case + return type. For Lhs and Rhs arguments, it is easy because we just have to deal with Lhs=Rhs. It is a bit more tricky with the return type. We also have to deal with const volatile and references. + update of <boost/detail/container_fwd.hpp> + detailed testing. I fear this is too much for 1.50 but we may plan for 1.51? It would be a good idea to have this working. Frédéric

Frédéric Bron wrote
Hi Lorenzo,
1) I'd add something like this for each STL container that defines an operator==:
template< typename T, class Alloc = std::allocator<T> > struct has_equal_to< std::vector<T, Alloc> > : has_equal_to<T> {};
Halas, it is a bit more complicated because the operator traits have in fact 3 arguments: template <class Lhs, class Rhs=Lhs, class Ret=dont_care> struct has_equal_to : public true_type-or-false_type {};
So we have to deal with left hand side and right hand side case + return type. For Lhs and Rhs arguments, it is easy because we just have to deal with Lhs=Rhs. It is a bit more tricky with the return type. We also have to deal with const volatile and references.
Yes, I understand (the evil is in the details ;) ). At some point I might had some decent specialization code at least for has_equal_to within Boost.Contract. I'll take a look, otherwise I can implement something and run it by this ML.
+ update of <boost/detail/container_fwd.hpp>
We can do separate headers <boost/type_traits/std/vector.hpp>, etc if the above doesn't work.
+ detailed testing.
I fear this is too much for 1.50 but we may plan for 1.51? It would be a good idea to have this working.
Yes, plus I don't have much time now... I wanted to know that there's agreement in fixing this so if I spend time fixing it won't be lost. I'll see what I can do for 1.50, if not then we'll try for 1.51. Thanks. --Lorenzo -- View this message in context: http://boost.2283326.n4.nabble.com/boost-type-traits-has-equal-to-std-vector... Sent from the Boost - Dev mailing list archive at Nabble.com.

On Mon, Apr 30, 2012 at 9:08 PM, lcaminiti <lorcaminiti@gmail.com> wrote:
Yes, plus I don't have much time now... I wanted to know that there's agreement in fixing this so if I spend time fixing it won't be lost. I'll see what I can do for 1.50, if not then we'll try for 1.51.
Maybe this will make it in 1.52... let's start discussing how the specializations should be implemented. Let's start with std::vector as an example. Are these specializations enough or I've missed some test case (see main): #include <boost/type_traits/has_equal_to.hpp> // add these specializations to boost/type_traits/has_equal_to.hpp namespace std { // fwd decl so no need to include STL template< typename T, class Alloc > class vector; } // namespace std namespace boost { template< typename T, class Alloc > struct has_equal_to < std::vector<T, Alloc> > : has_equal_to<T> {}; template< typename T, class LhsAlloc, class RhsAlloc, typename Ret > struct has_equal_to < std::vector<T, LhsAlloc > , std::vector<T, RhsAlloc > , Ret > : has_equal_to<T, T, Ret> {}; } // namespace boost // test program #include <boost/detail/lightweight_test.hpp> #include <vector> #include <iostream> struct xyz {}; // this type has no operator== int main ( void ) { typedef std::vector<int> vi; typedef std::vector<xyz> vx; // vi i; // vx x; // i == i; // ok // x == x; // error // x == i; // error // i == x; // error // 1 tparam BOOST_TEST((boost::has_equal_to<vi>::value)); // ok: i == i BOOST_TEST((!boost::has_equal_to<vx>::value)); // error: x == x // 2 tparams BOOST_TEST((boost::has_equal_to<vi, vi>::value)); // ok: i == i BOOST_TEST((!boost::has_equal_to<vx, vx>::value)); // error: x == x BOOST_TEST((!boost::has_equal_to<vx, vi>::value)); // error: x == i BOOST_TEST((!boost::has_equal_to<vi, vx>::value)); // error: i == x // 3 tparams BOOST_TEST((boost::has_equal_to<vi, vi, char>::value)); // ok: i == i BOOST_TEST((!boost::has_equal_to<vx, vx, char>::value)); // error: x == x BOOST_TEST((!boost::has_equal_to<vx, vi, char>::value)); // error: x == i BOOST_TEST((!boost::has_equal_to<vi, vx, char>::value)); // error: i == x return boost::report_errors(); } Also, if I forward declare the templates I need from STL, I don't need to #include the STL. This way there is no draw back in declaring the specializations directly in boost/type_traits/has_equal_to.hpp (or, is there?) instead that in a separate header boost/type_traits/std/vector.hpp. The advantage of this is that boost::has_equal_to<vector<T> > will do what the user expects "out of the box" and without requiring the user to know and remember to include the extra header type_traits/std/vector.hpp. Thanks. --Lorenzo

On 04/07/12 15:12, Lorenzo Caminiti wrote: <snip>
Also, if I forward declare the templates I need from STL, I don't need to #include the STL. This way there is no draw back in declaring the specializations directly in boost/type_traits/has_equal_to.hpp (or, is there?)
As discussed elsewhere in this thread, unfortunately it's not conformant to forward-declare things in the std namespace. There's a boost header boost/detail/container_fwd.hpp that will do it for those libraries which allow it, but as you can see if you look in it there are lots of cases where it's not possible. So some users will end up paying the cost of including those headers.
instead that in a separate header boost/type_traits/std/vector.hpp. The advantage of this is that boost::has_equal_to<vector<T> > will do what the user expects "out of the box" and without requiring the user to know and remember to include the extra header type_traits/std/vector.hpp.
I think the situation is a bit worse than you suggest. If the user sometimes forgets to include the extra header then his program might well end up violating the one definition rule. For that reason I would strongly recommend including the specializations along with the definition of has_equal_to, which unfortunately has the aforementioned cost of extra #includes in some cases. John Bytheway

Hi Lorenzo, Thanks for pushing this topic. Sorry to only replying now but it is hard to find some free time with 3 little children! I have looked a bit at the standard (ISO/IEC 14882, 2011). The relevant text is: 23.2.1, Table 96, 25.2.11. This is what I have found: if a and b are container<T> and t1 and t2 are T: - a==b: requires t1==t2 returning convertible to bool - a!=b: requires a==b - a<b: calls std::lexicographical_compare -> requires t1<t2 returning convertible to bool . a>b: requires b<a . a<=b: requires !(b<a) . a>=b: requires !(a<b) So in short: a==b and a!=b require t1==t2 and a<b, a>b, a<=b, a>=b require t1<t2 returning convertible to bool.
namespace std { // fwd decl so no need to include STL template< typename T, class Alloc > class vector; } // namespace std
Concerning your proposal to forward declare the containers, it is halas not allowed by the standard (17.6.4.2.1 Namespace std): "The behavior of a C++ program is undefined if it adds declarations or definitions to namespace std or to a namespace within namespace std unless otherwise specified." I imagine easily that it should behave as intended but it seems possible for an implementation to not like it.
template< typename T, class LhsAlloc, class RhsAlloc, typename Ret > struct has_equal_to < std::vector<T, LhsAlloc >, std::vector<T, RhsAlloc >, Ret > : has_equal_to<T, T, Ret> {};
The standard defines the operators only for identical allocators. operator== always returns bool and has_equal_to should return convertible to bool. This should be reflected in the declaration. Frédéric

On Sat, Jul 7, 2012 at 1:34 AM, Frédéric Bron <frederic.bron@m4x.org> wrote:
Hi Lorenzo,
Thanks for pushing this topic. Sorry to only replying now but it is hard to find some free time with 3 little children!
No problem. I also don't have much time to work on this but let's continue to discuss how it should be implemented then I'll put it in a release.
I have looked a bit at the standard (ISO/IEC 14882, 2011). The relevant text is: 23.2.1, Table 96, 25.2.11.
This is what I have found:
if a and b are container<T> and t1 and t2 are T: - a==b: requires t1==t2 returning convertible to bool - a!=b: requires a==b - a<b: calls std::lexicographical_compare -> requires t1<t2 returning convertible to bool . a>b: requires b<a . a<=b: requires !(b<a) . a>=b: requires !(a<b)
So in short: a==b and a!=b require t1==t2 and a<b, a>b, a<=b, a>=b require t1<t2 returning convertible to bool.
namespace std { // fwd decl so no need to include STL template< typename T, class Alloc > class vector; } // namespace std
Concerning your proposal to forward declare the containers, it is halas not allowed by the standard (17.6.4.2.1 Namespace std): "The behavior of a C++ program is undefined if it adds declarations or definitions to namespace std or to a namespace within namespace std unless otherwise specified."
:( Then all vector specializations go in a header boost/type_traits/std/vector.hpp (which will include <vector>), same for list, etc.
I imagine easily that it should behave as intended but it seems possible for an implementation to not like it.
template< typename T, class LhsAlloc, class RhsAlloc, typename Ret > struct has_equal_to < std::vector<T, LhsAlloc >, std::vector<T, RhsAlloc >, Ret > : has_equal_to<T, T, Ret> {};
The standard defines the operators only for identical allocators. operator== always returns bool and has_equal_to should return convertible to bool. This should be reflected in the declaration.
OK, how about this? 1) identical LHS and RHS containers (same T, Alloc, etc). 2) If no Ret then require bool, otherwise require convertible to bool. // In header: boost/type_traits/std/vector.hpp #include <boost/type_traits/has_equal_to.hpp> #include <boost/type_traits/is_convertible.hpp> #include <boost/mpl/and.hpp> #include <vector> // NOTE: Cannot fwd decl STL types (otherwise undefined behaviour). // From standard, let a and b be STL container<T>, and x and y be T, then: // * a == b defined requiring x == y convertible to bool // * a != b defined requiring !(a == b) // * a < b defined as std::lexicographical_compare that requires x < y // convertible to boo // * a > b defined requiring x > y // * a <= b defined requiring !(a > b) // * a >= b defined requiring !(a < b) namespace boost { template< typename T, class Alloc > struct has_equal_to < std::vector<T, Alloc>, std::vector<T, Alloc> > : has_equal_to<T, T, bool> {}; template< typename T, class Alloc, typename Ret > struct has_equal_to < std::vector<T, Alloc>, std::vector<T, Alloc>, Ret > : mpl::and_< has_equal_to<T, T, bool> , is_convertible<Ret, bool> > {}; // specializations for more STL containers here... } // namespace // -- header ends -- // test program #include <boost/detail/lightweight_test.hpp> #include <iostream> struct xyz {}; // this type has no operator== int main ( void ) { typedef std::vector<int> vi; typedef std::vector<xyz> vx; // vi i; // vx x; // i == i; // ok // x == x; // error // x == i; // error // i == x; // error // 1 tparam BOOST_TEST((boost::has_equal_to<vi>::value)); // ok: i == i BOOST_TEST((!boost::has_equal_to<vx>::value)); // error: x == x // 2 tparams BOOST_TEST((boost::has_equal_to<vi, vi>::value)); // ok: i == i BOOST_TEST((!boost::has_equal_to<vx, vx>::value)); // error: x == x BOOST_TEST((!boost::has_equal_to<vx, vi>::value)); // error: x == i BOOST_TEST((!boost::has_equal_to<vi, vx>::value)); // error: i == x // 3 tparams BOOST_TEST((boost::has_equal_to<vi, vi, char>::value)); // ok: i == i BOOST_TEST((!boost::has_equal_to<vx, vx, char>::value)); // error: x == x BOOST_TEST((!boost::has_equal_to<vx, vi, char>::value)); // error: x == i BOOST_TEST((!boost::has_equal_to<vi, vx, char>::value)); // error: i == x return boost::report_errors(); } Also, am I missing any relevant test? Thanks. --Lorenzo

Le 15/07/12 03:02, Lorenzo Caminiti a écrit : > On Sat, Jul 7, 2012 at 1:34 AM, Frédéric Bron <frederic.bron@m4x.org> wrote: >> I have looked a bit at the standard (ISO/IEC 14882, 2011). The >> relevant text is: 23.2.1, Table 96, 25.2.11. >> >> This is what I have found: >> >> if a and b are container<T> and t1 and t2 are T: >> - a==b: requires t1==t2 returning convertible to bool >> - a!=b: requires a==b >> - a<b: calls std::lexicographical_compare -> requires t1<t2 returning >> convertible to bool >> . a>b: requires b<a >> . a<=b: requires !(b<a) >> . a>=b: requires !(a<b) >> >> So in short: a==b and a!=b require t1==t2 and a<b, a>b, a<=b, a>=b >> require t1<t2 returning convertible to bool. >> >>> namespace std { // fwd decl so no need to include STL >>> template< typename T, class Alloc > class vector; >>> } // namespace std >> Concerning your proposal to forward declare the containers, it is >> halas not allowed by the standard (17.6.4.2.1 Namespace std): >> "The behavior of a C++ program is undefined if it adds declarations or >> definitions to namespace std or to a namespace within namespace std >> unless otherwise specified." > :( Then all vector specializations go in a header > boost/type_traits/std/vector.hpp (which will include <vector>), same > for list, etc. Hi Lorenzo, the trick is that every time you include <boost/type_traits/has_equal_to.hpp> you need to include all the specializations, otherwise you could have some parts of a program that contains the specialization and not others. This is unfortunate, but as the review of Boost.Conversion showed this is needed to maintain the ODR. Note also that the inclusion of the file and the specialization must be done only if the library is not conforming to the standard, and we can expect that future libraries will conform to, so the inclusion will not be requiered for the conforming versions. >> I imagine easily that it should behave as intended but it seems >> possible for an implementation to not like it. >> >>> template< typename T, class LhsAlloc, class RhsAlloc, typename Ret > >>> struct has_equal_to < std::vector<T, LhsAlloc >, std::vector<T, RhsAlloc >, Ret > : has_equal_to<T, T, Ret> >>> {}; >> The standard defines the operators only for identical allocators. >> operator== always returns bool and has_equal_to should return >> convertible to bool. This should be reflected in the declaration. > OK, how about this? > 1) identical LHS and RHS containers (same T, Alloc, etc). > 2) If no Ret then require bool, otherwise require convertible to bool. > > // In header: boost/type_traits/std/vector.hpp > #include <boost/type_traits/has_equal_to.hpp> > #include <boost/type_traits/is_convertible.hpp> > #include <boost/mpl/and.hpp> > #include <vector> > > // NOTE: Cannot fwd decl STL types (otherwise undefined behaviour). > // From standard, let a and b be STL container<T>, and x and y be T, then: > // * a == b defined requiring x == y convertible to bool > // * a != b defined requiring !(a == b) > // * a < b defined as std::lexicographical_compare that requires x < y > // convertible to boo > // * a > b defined requiring x > y > // * a <= b defined requiring !(a > b) > // * a >= b defined requiring !(a < b) > > namespace boost { > > template< typename T, class Alloc > > struct has_equal_to < std::vector<T, Alloc>, std::vector<T, Alloc> > : > has_equal_to<T, T, bool> > {}; I think bool need to be added here template< typename T, class Alloc > struct has_equal_to < std::vector<T, Alloc>, std::vector<T, Alloc>, bool > : has_equal_to<T, T, bool> {}; > template< typename T, class Alloc, typename Ret > > struct has_equal_to < std::vector<T, Alloc>, std::vector<T, Alloc>, Ret > : > mpl::and_< > has_equal_to<T, T, bool> > , is_convertible<Ret, bool> > > > {}; I'm not sure this specialization is needed. The convertibility to bool is already described with the first specialization, as has_equal_to test for convertibility to the result, not is_same. > // specializations for more STL containers here... > > } // namespace > > // -- header ends -- > > // test program > > #include <boost/detail/lightweight_test.hpp> > #include <iostream> > > struct xyz {}; // this type has no operator== > > int main ( void ) > { > typedef std::vector<int> vi; > typedef std::vector<xyz> vx; > > // vi i; > // vx x; > // i == i; // ok > // x == x; // error > // x == i; // error > // i == x; // error > > // 1 tparam > BOOST_TEST((boost::has_equal_to<vi>::value)); // ok: i == i > BOOST_TEST((!boost::has_equal_to<vx>::value)); // error: x == x > // 2 tparams > BOOST_TEST((boost::has_equal_to<vi, vi>::value)); // ok: i == i > BOOST_TEST((!boost::has_equal_to<vx, vx>::value)); // error: x == x > BOOST_TEST((!boost::has_equal_to<vx, vi>::value)); // error: x == i > BOOST_TEST((!boost::has_equal_to<vi, vx>::value)); // error: i == x > // 3 tparams > BOOST_TEST((boost::has_equal_to<vi, vi, char>::value)); // ok: i == i > BOOST_TEST((!boost::has_equal_to<vx, vx, char>::value)); // error: x == x > BOOST_TEST((!boost::has_equal_to<vx, vi, char>::value)); // error: x == i > BOOST_TEST((!boost::has_equal_to<vi, vx, char>::value)); // error: i == x > > return boost::report_errors(); > } > > Also, am I missing any relevant test? > > You are missing the Allocator parameter. I'm not sure the following works. BOOST_TEST((boost::has_equal_to<vi, vi, char>::value)); // ok: i == i In any case, I don't think this should be a constraint. Best, Vicente

On Sun, Jul 15, 2012 at 3:10 AM, Vicente J. Botet Escriba <vicente.botet@wanadoo.fr> wrote:
the trick is that every time you include <boost/type_traits/has_equal_to.hpp> you need to include all the specializations, otherwise you could have some parts of a program that contains the specialization and not others. This is unfortunate, but as the review of Boost.Conversion showed this is needed to maintain the ODR. Note
Then the specializations have to go into boost/type_traits/has_equal_to.hpp which will have to include <vector> :(
also that the inclusion of the file and the specialization must be done only if the library is not conforming to the standard, and we can expect that future libraries will conform to, so the inclusion will not be requiered for the conforming versions.
I don't think this is true... the specializations will always be needed unless future standards remove operator== (<, etc) from the overloaded set using SFINAE when T doesn't have an operator== (<, etc). But based on C++03 and C++11, the STL defines operator== and the has_equal_to (has_less_than, etc) specializations are needed.
OK, how about this? 1) identical LHS and RHS containers (same T, Alloc, etc). 2) If no Ret then require bool, otherwise require convertible to bool.
// In header: boost/type_traits/std/vector.hpp #include <boost/type_traits/has_equal_to.hpp> #include <boost/type_traits/is_convertible.hpp> #include <boost/mpl/and.hpp> #include <vector>
// NOTE: Cannot fwd decl STL types (otherwise undefined behaviour). // From standard, let a and b be STL container<T>, and x and y be T, then: // * a == b defined requiring x == y convertible to bool // * a != b defined requiring !(a == b) // * a < b defined as std::lexicographical_compare that requires x < y // convertible to boo // * a > b defined requiring x > y // * a <= b defined requiring !(a > b) // * a >= b defined requiring !(a < b)
namespace boost {
template< typename T, class Alloc > struct has_equal_to < std::vector<T, Alloc>, std::vector<T, Alloc> > : has_equal_to<T, T, bool> {};
I think bool need to be added here
template< typename T, class Alloc > struct has_equal_to < std::vector<T, Alloc>, std::vector<T, Alloc>, bool > :
has_equal_to<T, T, bool> {};
Not IMO because then only has_equal_to<vector<int>, vector<int>, bool> will work. As a user I want to use has_equal_to<vector<int>, vector<int> > without the return type and for it to be smart enough to use the correct result type, which is bool in this case. So I think users will expect this specialization: template< typename T, class Alloc > struct has_equal_to < std::vector<T, Alloc>, std::vector<T, Alloc> /* no bool here */ > : has_equal_to<T, T, bool> {};
template< typename T, class Alloc, typename Ret > struct has_equal_to < std::vector<T, Alloc>, std::vector<T, Alloc>, Ret > : mpl::and_< has_equal_to<T, T, bool> , is_convertible<Ret, bool> > {};
I'm not sure this specialization is needed. The convertibility to bool is already described with the first specialization, as has_equal_to test for convertibility to the result, not is_same.
// specializations for more STL containers here...
} // namespace
// -- header ends --
// test program
#include <boost/detail/lightweight_test.hpp> #include <iostream>
struct xyz {}; // this type has no operator==
int main ( void ) { typedef std::vector<int> vi; typedef std::vector<xyz> vx;
// vi i; // vx x; // i == i; // ok // x == x; // error // x == i; // error // i == x; // error
// 1 tparam BOOST_TEST((boost::has_equal_to<vi>::value)); // ok: i == i BOOST_TEST((!boost::has_equal_to<vx>::value)); // error: x == x // 2 tparams BOOST_TEST((boost::has_equal_to<vi, vi>::value)); // ok: i == i BOOST_TEST((!boost::has_equal_to<vx, vx>::value)); // error: x == x BOOST_TEST((!boost::has_equal_to<vx, vi>::value)); // error: x == i BOOST_TEST((!boost::has_equal_to<vi, vx>::value)); // error: i == x // 3 tparams BOOST_TEST((boost::has_equal_to<vi, vi, char>::value)); // ok: i == i BOOST_TEST((!boost::has_equal_to<vx, vx, char>::value)); // error: x == x BOOST_TEST((!boost::has_equal_to<vx, vi, char>::value)); // error: x == i BOOST_TEST((!boost::has_equal_to<vi, vx, char>::value)); // error: i == x
return boost::report_errors(); }
Also, am I missing any relevant test?
You are missing the Allocator parameter.
The allocator parameter for both LHS and RHS always has to be the same... is it important to test using not the default allocator? Maybe I should test with different allocator paramters and make sure it returns false - I'll add these tests.
I'm not sure the following works.
BOOST_TEST((boost::has_equal_to<vi, vi, char>::value)); // ok: i == i
In any case, I don't think this should be a constraint.
Thanks a lot! --Lorenzo
participants (11)
-
Dave Abrahams
-
Frédéric Bron
-
Howard Hinnant
-
Ion Gaztañaga
-
Jeffrey Lee Hellrung, Jr.
-
John Bytheway
-
John Maddock
-
lcaminiti
-
Lorenzo Caminiti
-
Vicente Botet
-
Vicente J. Botet Escriba