
Hello all, common_type<T, U>::type is defined in a way that gives a hard compiler error if T and U do not have a common type (this is also how common_type is implemented in C++11). An alternative design (I'm not sure if implementable) would have been to not define the type member in case T and U have no common type. This way I could use SFINAE to detect if common_type<T, U>::type does not exist and therefore if T and U have not common type -- so to implement N3351's concept Common<T, U>. Is there a way to implement this alternative design? Thanks. --Lorenzo

On Thu, Oct 4, 2012 at 3:28 AM, Lorenzo Caminiti <lorcaminiti@gmail.com>wrote:
An alternative design (I'm not sure if implementable) would have been to not define the type member in case T and U have no common type. This way I could use SFINAE to detect if common_type<T, U>::type does not exist and therefore if T and U have not common type -- so to implement N3351's concept Common<T, U>.
Is there a way to implement this alternative design?
Yes, here you go: http://codepaste.net/xom5rd -- -Matt Calabrese

On Thu, Oct 4, 2012 at 3:46 AM, Matt Calabrese <rivorus@gmail.com> wrote:
Yes, here you go:
-- -Matt Calabrese
Woops, should have tested... I had a typo, but after fixing it's causing both clang and GCC to crash, I'll bump in a minute with something that doesn't die. -- -Matt Calabrese

On Thu, Oct 4, 2012 at 3:55 AM, Matt Calabrese <rivorus@gmail.com> wrote:
Woops, should have tested... I had a typo, but after fixing it's causing both clang and GCC to crash, I'll bump in a minute with something that doesn't die.
Updated and tested for edge cases int GCC and Clang. -- -Matt Calabrese

On Thu, Oct 4, 2012 at 1:17 AM, Matt Calabrese <rivorus@gmail.com> wrote:
On Thu, Oct 4, 2012 at 4:13 AM, Matt Calabrese <rivorus@gmail.com> wrote:
Updated and tested for edge cases int GCC and Clang.
Updated one more time to simplify it a little. I'm going to sleep, otherwise I'll keep changing it.
Thanks a lot! template< class > struct always_void { typedef void type; }; template< class T, class U, class = void > struct common_type_impl_2 {}; // (1) template< class T, class U > struct common_type_impl_2 // (2) < T, U , typename always_void < decltype( true ? std::declval< T >() : std::declval< U >() ) >::type // (*) > { typedef decltype( true ? std::declval< T >() : std::declval< U >() ) type; }; So I understand it :) Would you explain me who the 2nd common_type_impl2 get selected when there's a common type given that always_void<...>::type is always void? Is it because when decltype( true ? std::declval< T >() : std::declval< U >() ) >::type is not valid in the instatiation of always_void (*) then SFINAE removes the overload (2) and (1) gets selected for being the only choice left? If so, why (1) has the lower priority in being selected when there's a common type and both (1) and (2) are valid? Thanks! --Lorenzo

On Thu, Oct 4, 2012 at 12:19 PM, Lorenzo Caminiti <lorcaminiti@gmail.com>wrote:
On Thu, Oct 4, 2012 at 1:17 AM, Matt Calabrese <rivorus@gmail.com> wrote:
On Thu, Oct 4, 2012 at 4:13 AM, Matt Calabrese <rivorus@gmail.com> wrote:
Updated and tested for edge cases int GCC and Clang.
Updated one more time to simplify it a little. I'm going to sleep, otherwise I'll keep changing it.
Thanks a lot!
template< class > struct always_void { typedef void type; };
template< class T, class U, class = void > struct common_type_impl_2 {}; // (1)
template< class T, class U > struct common_type_impl_2 // (2) < T, U , typename always_void < decltype( true ? std::declval< T >() : std::declval< U >() ) >::type // (*) > { typedef decltype( true ? std::declval< T >() : std::declval< U >() ) type; };
So I understand it :) Would you explain me who the 2nd common_type_impl2 get selected when there's a common type given that always_void<...>::type is always void?
Is it because when decltype( true ? std::declval< T >() : std::declval< U >() ) >::type is not valid in the instatiation of always_void (*) then SFINAE removes the overload (2) and (1) gets selected for being the only choice left? If so, why (1) has the lower priority in being selected when there's a common type and both (1) and (2) are valid?
When the expression isn't valid, then substitution when trying to match the specialization fails, meaning it falls back to the default defintiion. When the expression is valid, the specialization is a better match because the last parameter (void) is an exact match with the template parameters that were passed (void was implicitly passed as the default parameter because of the original template declaration). It's the same way enable_if works only we don't need a boolean condition since we want the specialization to be picked for any time the expression is valid, regardless of the result type. -- -Matt Calabrese

On Thu, Oct 4, 2012 at 1:28 PM, Matt Calabrese <rivorus@gmail.com> wrote:
When the expression isn't valid, then substitution when trying to match the specialization fails, meaning it falls back to the default defintiion. When the expression is valid, the specialization is a better match because the last parameter (void) is an exact match with the template parameters that were passed (void was implicitly passed as the default parameter because of the original template declaration). It's the same way enable_if works only we don't need a boolean condition since we want the specialization to be picked for any time the expression is valid, regardless of the result type.
I made some enable if macros a while ago to make expresion checks like this simpler, I'll boostify them and put them up on the sandbox. -- -Matt Calabrese

On Thu, Oct 4, 2012 at 1:34 PM, Matt Calabrese <rivorus@gmail.com> wrote:
I made some enable if macros a while ago to make expresion checks like this simpler, I'll boostify them and put them up on the sandbox.
The enable_if macros are now in the sandbox in the directory enable_if. It requires the latest Boost.Preprocessor from trunk (code was recently added that I now depend on), and the variadic_macro_data library also in the sandbox. Documentation is in the sandbox but also hosted here: http://enableif.nfshost.com/ I'll probably add metafunction generators similar to MPL's HAS_XXX macros but for expression validation. Sorry that the examples are so poor in the documentation, I just reference the tests for the time being, assuming that if you want to use the library at this point you are really only interested in examples for the syntax anyway. -- -Matt Calabrese

On 10/05/2012 05:56 AM, Matt Calabrese wrote:
Documentation is in the sandbox but also hosted here: http://enableif.nfshost.com/
I hadn't seen this, nice quote.

On Thu, Oct 4, 2012 at 10:28 AM, Matt Calabrese <rivorus@gmail.com> wrote:
When the expression isn't valid, then substitution when trying to match the specialization fails, meaning it falls back to the default defintiion. When the expression is valid, the specialization is a better match because the last parameter (void) is an exact match with the template parameters that were passed (void was implicitly passed as the default parameter because of the original template declaration). It's the same way enable_if works only we don't need a boolean condition since we want the specialization to be picked for any time the expression is valid, regardless of the result type.
Got it, thanks! --Lorenzo

On Thu, Oct 4, 2012 at 12:28 AM, Lorenzo Caminiti <lorcaminiti@gmail.com>wrote:
Hello all,
common_type<T, U>::type is defined in a way that gives a hard compiler error if T and U do not have a common type (this is also how common_type is implemented in C++11).
An alternative design (I'm not sure if implementable) would have been to not define the type member in case T and U have no common type. This way I could use SFINAE to detect if common_type<T, U>::type does not exist and therefore if T and U have not common type -- so to implement N3351's concept Common<T, U>.
Is there a way to implement this alternative design?
Do you also need this in C++03? - Jeff

On Thu, Oct 4, 2012 at 6:02 AM, Jeffrey Lee Hellrung, Jr. <jeffrey.hellrung@gmail.com> wrote:
On Thu, Oct 4, 2012 at 12:28 AM, Lorenzo Caminiti <lorcaminiti@gmail.com>wrote:
Hello all,
common_type<T, U>::type is defined in a way that gives a hard compiler error if T and U do not have a common type (this is also how common_type is implemented in C++11).
An alternative design (I'm not sure if implementable) would have been to not define the type member in case T and U have no common type. This way I could use SFINAE to detect if common_type<T, U>::type does not exist and therefore if T and U have not common type -- so to implement N3351's concept Common<T, U>.
Is there a way to implement this alternative design?
Do you also need this in C++03?
No, C++11 only and thanks a lot to Matt for the code! --Lorenzo

Le 04/10/12 15:02, Jeffrey Lee Hellrung, Jr. a écrit :
On Thu, Oct 4, 2012 at 12:28 AM, Lorenzo Caminiti <lorcaminiti@gmail.com>wrote:
Hello all,
common_type<T, U>::type is defined in a way that gives a hard compiler error if T and U do not have a common type (this is also how common_type is implemented in C++11).
An alternative design (I'm not sure if implementable) would have been to not define the type member in case T and U have no common type. This way I could use SFINAE to detect if common_type<T, U>::type does not exist and therefore if T and U have not common type -- so to implement N3351's concept Common<T, U>.
Is there a way to implement this alternative design?
Do you also need this in C++03?
Even if Lorenzo doesn't needs it, it will be great to have it in Boost. Jeffrey, do you have something that is working already? Best, Vicente

On Thu, Oct 4, 2012 at 9:15 AM, Vicente J. Botet Escriba < vicente.botet@wanadoo.fr> wrote:
Le 04/10/12 15:02, Jeffrey Lee Hellrung, Jr. a écrit :
On Thu, Oct 4, 2012 at 12:28 AM, Lorenzo Caminiti <lorcaminiti@gmail.com
wrote:
Hello all,
common_type<T, U>::type is defined in a way that gives a hard compiler error if T and U do not have a common type (this is also how common_type is implemented in C++11).
An alternative design (I'm not sure if implementable) would have been to not define the type member in case T and U have no common type. This way I could use SFINAE to detect if common_type<T, U>::type does not exist and therefore if T and U have not common type -- so to implement N3351's concept Common<T, U>.
Is there a way to implement this alternative design?
Do you also need this in C++03?
Even if Lorenzo doesn't needs it, it will be great to have it in Boost. Jeffrey, do you have something that is working already?
I don't yet, but this should be fairly straightforward based on the present C++03 implementation. I guess I can try to get this in this weekend? - Jeff

On Thu, Oct 4, 2012 at 8:56 PM, Jeffrey Lee Hellrung, Jr. < jeffrey.hellrung@gmail.com> wrote:
On Thu, Oct 4, 2012 at 9:15 AM, Vicente J. Botet Escriba < vicente.botet@wanadoo.fr> wrote:
Le 04/10/12 15:02, Jeffrey Lee Hellrung, Jr. a écrit :
On Thu, Oct 4, 2012 at 12:28 AM, Lorenzo Caminiti <lorcaminiti@gmail.com
wrote:
Hello all,
common_type<T, U>::type is defined in a way that gives a hard compiler error if T and U do not have a common type (this is also how common_type is implemented in C++11).
An alternative design (I'm not sure if implementable) would have been to not define the type member in case T and U have no common type. This way I could use SFINAE to detect if common_type<T, U>::type does not exist and therefore if T and U have not common type -- so to implement N3351's concept Common<T, U>.
Is there a way to implement this alternative design?
Do you also need this in C++03?
Even if Lorenzo doesn't needs it, it will be great to have it in Boost. Jeffrey, do you have something that is working already?
I don't yet, but this should be fairly straightforward based on the present C++03 implementation. I guess I can try to get this in this weekend?
Never mind, I think I had misremembered what the implementation looks like; I don't see a straightforward way to effect this in the C++03 implementation :( - Jeff

Hello all, One a separate note, why the following SFINAE impl of EqualityComparable returns 0 on std::common_type<int, int>::type? EqualityComparable<int>::value // 1 -- ok EqualityComparable<std::common_type<int>::type>::value // 1 -- ok EqualityComparable<std::common_type<int, int>::type>::value // 0 -- why?? Code: namespace std { class type_info; } #include <boost/type_traits/detail/yes_no_type.hpp> #include <iostream> #include <type_traits> template< typename _1 > struct EqualityComparable { struct requires0 { template< typename T > static boost::type_traits::yes_type check ( T a, T b, char (*) [sizeof(decltype( bool{a == b} ))] ); template< typename T > static boost::type_traits::no_type check ( ... ); template< typename T > struct sizeof_check { T a; T b; static size_t const value = sizeof check<_1>(a , b , 0); }; static bool const value = sizeof(boost::type_traits::yes_type) == sizeof_check< _1 >::value; }; static bool const value = requires0::value; }; int main ( void ) { std::cout << EqualityComparable<int>::value << std::endl; // 1 -- ok std::cout << EqualityComparable<std::common_type<int>::type>::value << std::endl; // 1 -- ok std::cout << EqualityComparable<std::common_type<int, int>::type>::value << std::endl; // 0 -- why?? return 0; } I'm using Clang++ 3.2. Thanks, --Lorenzo

On Fri, Oct 5, 2012 at 2:08 PM, Lorenzo Caminiti <lorcaminiti@gmail.com>wrote:
Hello all,
One a separate note, why the following SFINAE impl of EqualityComparable returns 0 on std::common_type<int, int>::type?
EqualityComparable<int>::value // 1 -- ok EqualityComparable<std::common_type<int>::type>::value // 1 -- ok
EqualityComparable<std::common_type<int, int>::type>::value // 0 -- why??
common_type< int, int >::type would be int&&. In your EqualityComparable implementation, your function parameter types are the argument of EqualityComparable directly, which is an r-value reference type. In your explicit call to check<_1>, you are passing "a" and "b" directly, but the parameter types of check expect r-value references. In order for this to work, you'd need to use std::forward. -- -Matt Calabrese

On Fri, Oct 5, 2012 at 11:36 AM, Matt Calabrese <rivorus@gmail.com> wrote:
On Fri, Oct 5, 2012 at 2:08 PM, Lorenzo Caminiti <lorcaminiti@gmail.com>wrote:
Hello all,
One a separate note, why the following SFINAE impl of EqualityComparable returns 0 on std::common_type<int, int>::type?
EqualityComparable<int>::value // 1 -- ok EqualityComparable<std::common_type<int>::type>::value // 1 -- ok
EqualityComparable<std::common_type<int, int>::type>::value // 0 -- why??
common_type< int, int >::type would be int&&. In your EqualityComparable implementation, your function parameter types are the argument of EqualityComparable directly, which is an r-value reference type. In your explicit call to check<_1>, you are passing "a" and "b" directly, but the parameter types of check expect r-value references. In order for this to work, you'd need to use std::forward.
I see, thanks! --Lorenzo

Hello, 05.10.2012 22:08, Lorenzo Caminiti wrote:
One a separate note, why the following SFINAE impl of EqualityComparable returns 0 on std::common_type<int, int>::type?
gcc-4.5.1 returns 1. Try this test on Clang: http://ideone.com/Pdube EqualityComparable<remove_refs<std::common_type<int,int>::type>::type>::value returns 1 on Clang 3.2. Note difference in printed types on GCC/Clang (rval ref). Best Regards, Evgeny

common_type<T, U>::type is defined in a way that gives a hard compiler error if T and U do not have a common type (this is also how common_type is implemented in C++11).
Sorry. This discussion slipped under my radar. I had to do this too. Implementation is here, starting around line 380: http://code.google.com/p/origin/source/browse/trunk/origin/type/traits.hpp I used this library to test the concepts we wrote in n3351, although that specific work was moved to a branch or the sandbox a while ago. I forget. Andrew

Lorenzo Caminiti <lorcaminiti <at> gmail.com> writes:
Hello all,
common_type<T, U>::type is defined in a way that gives a hard compiler error if T and U do not have a common type (this is also how common_type is implemented in C++11).
An alternative design (I'm not sure if implementable) would have been to not define the type member in case T and U have no common type. This way I could use SFINAE to detect if common_type<T, U>::type does not exist and therefore if T and U have not common type -- so to implement N3351's concept Common<T, U>.
Is there a way to implement this alternative design?
While this is not directly linked to your question, I have implemented some concepts using SFINAE so you can write stuff like: template <typename T> typename std::enable_if<LessThanComparable<T>::value>::type func() { } What I did works only in C++11. You may take a look at what I did here: www.github.com/ldionne/duck I'll be glad if this can be of any help. Please do not hesitate to contact me for anything. Louis Dionne

On Mon, Oct 8, 2012 at 6:44 AM, Louis Dionne <louis.dionne92@gmail.com> wrote:
Lorenzo Caminiti <lorcaminiti <at> gmail.com> writes:
Hello all,
common_type<T, U>::type is defined in a way that gives a hard compiler error if T and U do not have a common type (this is also how common_type is implemented in C++11).
An alternative design (I'm not sure if implementable) would have been to not define the type member in case T and U have no common type. This way I could use SFINAE to detect if common_type<T, U>::type does not exist and therefore if T and U have not common type -- so to implement N3351's concept Common<T, U>.
Is there a way to implement this alternative design?
While this is not directly linked to your question, I have implemented some concepts using SFINAE so you can write stuff like: template <typename T> typename std::enable_if<LessThanComparable<T>::value>::type func() { }
What I did works only in C++11. You may take a look at what I did here: www.github.com/ldionne/duck
I'll be glad if this can be of any help. Please do not hesitate to contact me for anything.
Thanks. FYI, this is definitely relevant to the email thread "[boost] [contract] toward N3351 concepts" and probably to the one about Boost.Generic as well. --Lorenzo
participants (8)
-
Andrew Sutton
-
Evgeny Panasyuk
-
Jeffrey Lee Hellrung, Jr.
-
Lorenzo Caminiti
-
Louis Dionne
-
Mathias Gaunard
-
Matt Calabrese
-
Vicente J. Botet Escriba