[type_traits] request for decayed<T>::type

Hi, The following trait is supposed to make it easier to forward arguments in generic code and in particular string literals. For example, I will use it when I update boost.assign to use const T& arguments instead of T arguments. It could also be used to make a better version of std::make_pair(): template< class F, class S > inline std::pair< typename decayed<F>::type, typename decayed<S>::type > make_pair( const F& f, const S& s ) { return std::pair< typename decayed<F>::type, typename decayed<S>::type >( f, s ); } Any thoughts from the type_traits authors? Do anybody mind if I add this to the cvs and make some docs for the type-traits library? br -Thorsten begin 666 decayed.cpp M(VEN8VQU9&4@/&)O;W-T+W)A;F=E+W9A;'5E7W1Y<&4N:'!P/@T*(VEN8VQU M9&4@/&)O;W-T+W1Y<&5?=')A:71S+VES7V%R<F%Y+FAP<#X-"B-I;F-L=61E M(#QB;V]S="]M<&PO979A;%]I9BYH<' ^#0HC:6YC;'5D92 \8F]O<W0O;7!L M+VED96YT:71Y+FAP<#X-"B-I;F-L=61E(#QU=&EL:71Y/@T*#0IN86UE<W!A M8V4@8F]O<W0-"GL-"B @("!N86UE<W!A8V4@9&5C87EE9%]D971A:6P-"B @ M("![#0H@(" @(" @('1E;7!L871E/"!C;&%S<R!4(#X-"B @(" @(" @<W1R M=6-T(')A;F=E7W!O:6YT97(-"B @(" @(" @>PT*(" @(" @(" @(" @='EP M961E9B!C;VYS="!T>7!E;F%M92!R86YG95]V86QU93Q4/CHZ='EP92H@='EP M93L-"B @(" @(" @?3L-"B @("!]#0H@(" @#0H@(" @=&5M<&QA=&4\(&-L M87-S(%0@/@T*(" @('-T<G5C="!D96-A>65D#0H@(" @>PT*(" @(" @("!T M>7!E9&5F('1Y<&5N86UE(&UP;#HZ979A;%]I9E]C/"!I<U]A<G)A>3Q4/CHZ M=F%L=64L#0H@(" @(" @(" @(" @(" @(" @(" @(" @(" @(" @(" @(" @ M(" @(&1E8V%Y961?9&5T86EL.CIR86YG95]P;VEN=&5R/%0^+ T*(" @(" @ M(" @(" @(" @(" @(" @(" @(" @(" @(" @(" @(" @("!M<&PZ.FED96YT M:71Y/%0^(#XZ.G1Y<&4@='EP93L-"B @("!].PT*#0H@(" @=&5M<&QA=&4\ M(&-L87-S($8L(&-L87-S(%,@/@T*(" @(&EN;&EN92!S=&0Z.G!A:7(\('1Y M<&5N86UE(&1E8V%Y960\1CXZ.G1Y<&4L( T*(" @(" @(" @(" @(" @(" @ M(" @('1Y<&5N86UE(&1E8V%Y960\4SXZ.G1Y<&4@/@T*(" @(&UA:V5?<&%I M<B@@8V]N<W0@1B8@9BP@8V]N<W0@4R8@<R I#0H@(" @>PT*(" @(" @("!R M971U<FX@<W1D.CIP86ER/"!T>7!E;F%M92!D96-A>65D/$8^.CIT>7!E+" - M"B @(" @(" @(" @(" @(" @(" @(" @(" @='EP96YA;64@9&5C87EE9#Q3 M/CHZ='EP92 ^*"!F+"!S("D[( T*(" @('T-"@T*?0T*#0HC:6YC;'5D92 \ M<W1R:6YG/@T*#0II;G0@;6%I;B@I#0I[#0H@(" @8F]O<W0Z.F1E8V%Y960\ M:6YT/CHZ='EP92!I;G1E9V5R(#T@,3L-"B @("!B;V]S=#HZ9&5C87EE9#QC M:&%R6S)=/CHZ='EP92!C:&%R7W!T<B ](")F;V\B.PT*(" @(&)O;W-T.CID M96-A>65D/&-O;G-T(&-H87);,ET^.CIT>7!E(&-C:&%R7W!T<B ](")F;V\B M.PT*(" @(&)O;W-T.CID96-A>65D/'=C:&%R7W1;,ET^.CIT>7!E('=C:&%R M7W!T<B ]($PB9F]O(CL-"B @("!B;V]S=#HZ9&5C87EE9#QC;VYS="!W8VAA M<E]T6S)=/CHZ='EP92!C=V-H87)?<'1R(#T@3")F;V\B.PT*#0H@(" @<W1D M.CIP86ER/'-T9#HZ<W1R:6YG+'-T9#HZ<W1R:6YG/B!P(" ](&)O;W-T.CIM M86ME7W!A:7(H(")F;V\B+" B8F%R(B I.PT*(" @('-T9#HZ<&%I<CQS=&0Z M.G-T<FEN9RP@:6YT/B @(" @(" @<#(@/2!B;V]S=#HZ;6%K95]P86ER*" B M9F]O(BP@,2 I.PT*#0H@(" @<W1D.CIP86ER/'-T9#HZ=W-T<FEN9RQS=&0Z M.G-T<FEN9SX@<#,@(#T@8F]O<W0Z.FUA:V5?<&%I<B@@3")F;V\B+" B8F%R M(B I.PT*(" @('-T9#HZ<&%I<CQS=&0Z.G=S=')I;F<L(&EN=#X@(" @(" @ M(' T(" ](&)O;W-T.CIM86ME7W!A:7(H($PB9F]O(BP@,2 I.PT*#0HO+R @ M("!I;G0@87)R87E;,3!=.PT*+R\@(" @<W1D.CIP86ER/&EN="HL:6YT*CX@ M<#4@/2!B;V]S=#HZ;6%K95]P86ER*"!A<G)A>2P@87)R87D@*3L-"@T*?0T* ` end

"Thorsten Ottosen" wrote:
Attached is file which works on VC6 / Intel 7 / BCB6.4. Only tiny changes were needed. This boost::make_pair would be very useful. Questions: - maybe other name than decayed could be found - isn't parameter passing suboptimal for e.g. boost::make_pair(0, 0)? - following compiles: struct ABC { explicit ABC(int) {} }; std::pair<ABC, ABC> p5 = boost::make_pair(1, 2); std::pair<ABC, ABC> p5 = std::make_pair(1, 2); // also compiles Maybe the boost::make_pair could be coded somehow to disable this behavior (ABC constructor is explicit). /Pavel begin 666 decayed.cpp M(VEN8VQU9&4@/&)O;W-T+W)A;F=E+W9A;'5E7W1Y<&4N:'!P/@T*(VEN8VQU M9&4@/&)O;W-T+W1Y<&5?=')A:71S+VES7V%R<F%Y+FAP<#X-"B-I;F-L=61E M(#QB;V]S="]M<&PO979A;%]I9BYH<' ^#0HC:6YC;'5D92 \8F]O<W0O;7!L M+VED96YT:71Y+FAP<#X-"B-I;F-L=61E(#QU=&EL:71Y/@T*#0IN86UE<W!A M8V4@8F]O<W0-"GL-"B @("!N86UE<W!A8V4@9&5C87EE9%]D971A:6P-"B @ M("![#0H@(" @(" @('1E;7!L871E/"!C;&%S<R!4(#X-"B @(" @(" @<W1R M=6-T(')A;F=E7W!O:6YT97(-"B @(" @(" @>PT*(" @(" @(" @(" @='EP M961E9B!C;VYS="!T>7!E;F%M92!R86YG95]V86QU93Q4/CHZ='EP92H@='EP M93L-"B @(" @(" @?3L-"B @("!]#0H@(" @#0H@(" @=&5M<&QA=&4\(&-L M87-S(%0@/@T*(" @('-T<G5C="!D96-A>65D#0H@(" @>PT*(" @(" @("!T M>7!E9&5F('1Y<&5N86UE(&UP;#HZ979A;%]I9E]C/"!I<U]A<G)A>3Q4/CHZ M=F%L=64L#0H@(" @(" @(" @(" @(" @(" @(" @(" @(" @(" @(" @(" @ M(" @(&1E8V%Y961?9&5T86EL.CIR86YG95]P;VEN=&5R/%0^+ T*(" @(" @ M(" @(" @(" @(" @(" @(" @(" @(" @(" @(" @(" @("!M<&PZ.FED96YT M:71Y/%0^(#XZ.G1Y<&4@='EP93L-"B @("!].PT*#0H@(" @=&5M<&QA=&4\ M(&-L87-S($8L(&-L87-S(%,@/@T*(" @(&EN;&EN92!S=&0Z.G!A:7(\('1Y M<&5N86UE(&1E8V%Y960\1CXZ.G1Y<&4L( T*(" @(" @(" @(" @(" @(" @ M(" @('1Y<&5N86UE(&1E8V%Y960\4SXZ.G1Y<&4@/@T*(" @(&UA:V5?<&%I M<B@@8V]N<W0@1B8@9BP@8V]N<W0@4R8@<R I#0H@(" @>PT*(" @(" @("!R M971U<FX@<W1D.CIP86ER/"!"3T]35%]$14150T5$7U194$5.04U%(&1E8V%Y M960\1CXZ.G1Y<&4L( T*(" @(" @(" @(" @(" @(" @(" @(" @("!"3T]3 M5%]$14150T5$7U194$5.04U%(&1E8V%Y960\4SXZ.G1Y<&4@/B@@9BP@<R I M.R -"B @("!]#0H-"GT-"@T*(VEN8VQU9&4@/'-T<FEN9SX-"@T*#0II;G0@ M;6%I;B@I#0I[#0HC:68@*&1E9FEN960@7U]"3U),04Y$0U]?*0T*(R @<')A M9VUA('=A<FX@+3@P,#0@+R\@;F5V97(@=7-E9"!V86QU97,-"B-E;F1I9@T* M(" @(&)O;W-T.CID96-A>65D/&EN=#XZ.G1Y<&4@:6YT96=E<B ](#$[#0H@ M(" @8F]O<W0Z.F1E8V%Y960\8VAA<ELR73XZ.G1Y<&4@8VAA<E]P='(@/2 B M9F]O(CL-"B @("!B;V]S=#HZ9&5C87EE9#QC;VYS="!C:&%R6S)=/CHZ='EP M92!C8VAA<E]P='(@/2 B9F]O(CL-"B @("!B;V]S=#HZ9&5C87EE9#QW8VAA M<E]T6S)=/CHZ='EP92!W8VAA<E]P='(@/2!,(F9O;R([#0H@(" @8F]O<W0Z M.F1E8V%Y960\8V]N<W0@=V-H87)?=%LR73XZ.G1Y<&4@8W=C:&%R7W!T<B ] M($PB9F]O(CL-"@T*(" @('-T9#HZ<&%I<CQS=&0Z.G-T<FEN9RQS=&0Z.G-T M<FEN9SX@<" @/2!B;V]S=#HZ;6%K95]P86ER*" B9F]O(BP@(F)A<B(@*3L- M"B @("!S=&0Z.G!A:7(\<W1D.CIS=')I;F<L(&EN=#X@(" @(" @(' R(#T@ M8F]O<W0Z.FUA:V5?<&%I<B@@(F9O;R(L(#$@*3L-"@T*(" @('-T9#HZ<&%I M<CQS=&0Z.G=S=')I;F<L<W1D.CIS=')I;F<^(' S(" ](&)O;W-T.CIM86ME M7W!A:7(H($PB9F]O(BP@(F)A<B(@*3L-"B @("!S=&0Z.G!A:7(\<W1D.CIW M<W1R:6YG+"!I;G0^(" @(" @("!P-" @/2!B;V]S=#HZ;6%K95]P86ER*"!, M(F9O;R(L(#$@*3L-"@T*+R\@(" @:6YT(&%R<F%Y6S$P73L-"B\O(" @('-T M9#HZ<&%I<CQI;G0J+&EN="H^(' U(#T@8F]O<W0Z.FUA:V5?<&%I<B@@87)R B87DL(&%R<F%Y("D[#0H@(" @<F5T=7)N(# [#0H-"GT-"@`` ` end

"Pavel Vozenilek" <pavel_vozenilek@hotmail.com> wrote in message news:cr6ga4 | Attached is file which works on VC6 / Intel 7 / BCB6.4. | Only tiny changes were needed. Thanks. | This boost::make_pair would be very useful. ok, it was not meant as a proposal, but just as an example of where it would be useful. | Questions: | | - maybe other name than decayed could be found | | - isn't parameter passing suboptimal for e.g. | boost::make_pair(0, 0)? by suboptimal you mean performance-wise? | - following compiles: | | struct ABC { | explicit ABC(int) {} | }; | std::pair<ABC, ABC> p5 = boost::make_pair(1, 2); | std::pair<ABC, ABC> p5 = std::make_pair(1, 2); // also compiles | | Maybe the boost::make_pair could be coded | somehow to disable this behavior (ABC constructor | is explicit). maybe, I do think a new make_pair() should be backwards compatible. -Thorsten

On Dec 22, 2004, at 8:29 AM, Thorsten Ottosen wrote:
I think decayed is an excellent trait. Your implementation should be modified to also take care of functions converting into function pointers though. It's a shame about the const qaulifier in your example make_pair messing up the int[10] conversion to int*. But with an eye towards the future, I think decayed will still eventually improve make_pair: template <class T, class U> inline pair<typename decayed<typename remove_reference<T>::type>::type, typename decayed<typename remove_reference<U>::type>::type> make_pair(T&& t, U&& u) { typedef typename decayed<typename remove_reference<T>::type>::type dT; typedef typename decayed<typename remove_reference<U>::type>::type dU; return pair<dT, dU>(std::forward<T>(t), std::forward<U>(u)); } This alternative will mimic the present (by value) make_pair, and work with: int array[10]; std::pair<int*,int*> p5 = std::make_pair( array, array ); It will also move from rvalue arguments such as string or vector making it more efficient (assuming a move-aware pair in the future). decayed has my vote for a useful trait (especially assuming an rvalue reference). On Jan 1, 2005, at 10:40 AM, Pavel Vozenilek wrote:
This is a characteristic of pair, not make_pair. And it could be disabled by restricting the member template constructor in: template <class T1, class T2> struct pair { typedef T1 first_type; typedef T2 second_type; T1 first; T2 second; pair(); pair(const T1& x, const T2& y); template<class U, class V> pair(const pair<U, V> &p); }; such that is_convertible<U, T1> and is_convertible<V, T2>, which is a change I would support. -Howard

I agree that it should work for function to function-pointer conversions as well. I've attached a slightly improved version that gets your non-const make_pair working, also uses remove_bounds rather than the range lib traits (just to keep things in the type_traits family). Not sure about the "decayed" name, but I can't think of anything better right now.
If we do that, then we should do the same for the tuple constructors as well. John.

On Jan 2, 2005, at 11:01 AM, John Maddock wrote:
Absolutely. In fact, I'd really like to go a step or two further (for C++0X, not tr1): Introduce a trait: template <class P1, class P2> struct tuple_convertible { static const bool value = ... }; Such that value is true only if both P1 and P2 are "tuple like" (there used to be trait for "tuple like"), and tuple_size<P1>::value == tuple_size<P2>::value, and for every element I in P1 and P2, is_convertible<tuple_element<I,P1>::type, tuple_element<I,P2>::type>. I.e. tuple_convertible answers true only if P1 and P2 are tuples of the same size and each member of P1 is convertible to the corresponding member of P2. And then pair could look like: template <class T1, class T2> struct pair { typedef T1 first_type; typedef T2 second_type; T1 first; T2 second; pair(); pair(const T1& x, const T2& y); // needed only for binary compatibility template <class U, class V> pair(U&& x, V&& y); // pair(const pair& p); // implicitly generated // pair& operator=(const pair& p); // implicitly generated pair(pair&& p); pair& operator=(pair&& p); template <class P> pair(P&& p, typename enable_if<tuple_convertible<P, pair>::value>::type* = 0); template <class P> pair(const P& p, typename enable_if<tuple_convertible<P, pair>::value>::type* = 0); template <class P> pair& operator=(P&& p); template <class P> pair& operator=(const P& p); void swap(pair&& p); }; Here is a move-aware pair capable of holding move-only types, of moving in heavy weight rvalues, of interoperability with all compatible tuple-like types, including tuple, array, complex, and simply other types of pair. is_convertible will answer correctly when asked about pair and some other tuple-like type. And explicit constructors of T1 and T2 will be respected as Pavel requests. And yes, I'd like to see at least tuple and complex outfitted similarly. array is a special case that may not be able to do this, unless we can start adding ctors to it via another core extension. Btw, I kind of like "decayed" for a name. -Howard

"Jonathan Turkanis" <technews@kangaroologic.com> wrote in message news:crccb6$i7f$1@sea.gmane.org... | Howard Hinnant wrote: | | > Btw, I kind of like "decayed" for a name. | | It occurred to me that decayed falls into the "transformations between types" | category, and so should be in the imperative for consistency with the other | transformations: decay<T>::type. yeah, I agree. -Thorsten

"John Maddock" <john@johnmaddock.co.uk> wrote in message news:022b01c4f0e4$48c92cd0$f72f0d52@fuji... | >> Any thoughts from the type_traits authors? | > | > I think decayed is an excellent trait. Your implementation should be | > modified to also take care of functions converting into function pointers | > though. It's a shame about the const qaulifier in your example make_pair | > messing up the int[10] conversion to int*. | | I agree that it should work for function to function-pointer conversions as | well. | | I've attached a slightly improved version that gets your non-const make_pair | working, also uses remove_bounds rather than the range lib traits (just to | keep things in the type_traits family). I can't see your attachment; would you mind post it again? Thanks -Thorsten

"Howard Hinnant" <hinnant@twcny.rr.com> wrote in message news:C94D8AC6-5C17-11D9-BA03-003065D18932@twcny.rr.com... | On Dec 22, 2004, at 8:29 AM, Thorsten Ottosen wrote: | But with | an eye towards the future, I think decayed will still eventually | improve make_pair: | | template <class T, class U> | inline | pair<typename decayed<typename remove_reference<T>::type>::type, | typename decayed<typename remove_reference<U>::type>::type> | make_pair(T&& t, U&& u) | { | typedef typename decayed<typename remove_reference<T>::type>::type | dT; | typedef typename decayed<typename remove_reference<U>::type>::type | dU; | return pair<dT, dU>(std::forward<T>(t), std::forward<U>(u)); | } why do you need remove_reference? If its needed, then maybe it should be part of decayed<T>'s behavior. -Thorsten

On Jan 3, 2005, at 1:49 PM, Thorsten Ottosen wrote:
remove_reference is needed because (in the rvalue reference proposal) T may be deduced as a reference type: A a; make_pair(a, a); // T and U deduced as A& make_pair(A(), A()); // T and U deduced as A I thought about putting remove_reference into decayed, but didn't think it fit semantically because references don't decay. It turns out that either I'm mistaken, or CodeWarrior decays references as an extension. This compiles for me: typedef int A[3]; void foo(int*) {} typedef void F(); typedef void (*FP)(); void bar(FP) {} void fun() {} int main() { A a = {}; A& ar = a; foo(ar); F f; F& fr = f; bar(fr); } Comeau seems to like it too. So now it looks to me like the remove_reference should go inside decayed, which happily does clean up the proposed make_pair a bit. Are we having a good day or what?! ;-) -Howard

"Howard Hinnant" <hinnant@twcny.rr.com> wrote in message news:E00E478E-5DC5-11D9-BC20-003065D18932@twcny.rr.com... | On Jan 3, 2005, at 1:49 PM, Thorsten Ottosen wrote: | | > | > "Howard Hinnant" <hinnant@twcny.rr.com> wrote in message | > news:C94D8AC6-5C17-11D9-BA03-003065D18932@twcny.rr.com... | > | On Dec 22, 2004, at 8:29 AM, Thorsten Ottosen wrote: | > | > | But with | > | an eye towards the future, I think decayed will still eventually | > | improve make_pair: | > | | > | template <class T, class U> | > | inline | > | pair<typename decayed<typename remove_reference<T>::type>::type, | > | typename decayed<typename remove_reference<U>::type>::type> | > | make_pair(T&& t, U&& u) [snip] | > why do you need remove_reference? If its needed, then maybe it should | > be | > part of decayed<T>'s behavior. | | remove_reference is needed because (in the rvalue reference proposal) T | may be deduced as a reference type: | | A a; | make_pair(a, a); // T and U deduced as A& | make_pair(A(), A()); // T and U deduced as A ok, I probably need to read up on move-semantics :-) I usually think of T as the deduced type in expressions like const T& etc. (To be honest, I don't fully get why the return type of std::forward() and std::move() can both be T&&, yet be different :-) ) | I thought about putting remove_reference into decayed, but didn't think | it fit semantically because references don't decay. It turns out that | either I'm mistaken, or CodeWarrior decays references as an extension. | This compiles for me: [snip] I dunno, IFAIK, the term decay is used when an array decays to a pointer and the original type is lost. I'm not sure if that is true with function pointers (ok, you loose the original address as a CT-value, but....). | typedef int A[3]; | | void foo(int*) {} | | typedef void F(); | | typedef void (*FP)(); | | void bar(FP) {} | | void fun() {} | | int main() | { | A a = {}; | A& ar = a; | foo(ar); | F f; | F& fr = f; | bar(fr); | } | | Comeau seems to like it too. So now it looks to me like the | remove_reference should go inside decayed, which happily does clean up | the proposed make_pair a bit. hm... you can always use a referece where a value is expected: the referenced value is copied. To get the behavior of the make_pair() example, you need to say void foo(int*&); void bar(FP&); | Are we having a good day or what?! ;-) We sure are :-) Anyway, the problem of the reference type doesn't seem to be a problem before we introduce move-semantics; when we do that, decay<T>::type should strip any reference IMO because it will be the most wanted behavior. br -Thorsten

Rambling a little bit today... I set up a little test: template <class T> struct A { A(const T&) {} }; template <class T> inline A<T> make_A(T t) { return A<T>(t); } void f() {} int main() { std::cout << typeid(make_A(0)).name() << '\n'; const volatile int i = 0; std::cout << typeid(make_A(i)).name() << '\n'; const int a3[3] = {}; std::cout << typeid(make_A(a3)).name() << '\n'; int a4[4] = {}; std::cout << typeid(make_A(a4)).name() << '\n'; std::cout << typeid(make_A(f)).name() << '\n'; std::cout << typeid(make_A("narrow")).name() << '\n'; std::cout << typeid(make_A(L"wide")).name() << '\n'; } This prints out (on my system): A<int> A<int> A<const int *> A<int *> A<void (*)()> A<const char *> A<const wchar_t *> I believe this output, using pass-by-value for make_A, can be considered "the gold standard" for decay<T>. And here's what you get if make_A simply uses pass-by-const-ref: template <class T> inline A<T> make_A(const T& t) { return A<T>(t); } A<int> A<volatile int> A<int[3]> A<int[4]> A<void ()> A<char[7]> A<wchar_t[5]> Using a decay that supports function->function pointer, and one that strips top level cv-qualifiers off of non-arrays, and non-functions, and using John's overload strategy: template <class T> inline A<typename decay<T>::type> make_A(T& t) { return A<typename decay<T>::type>(t); } template <class T> inline A<typename decay<const T>::type> make_A(const T& t) { return A<typename decay<const T>::type>(t); } I can get the original behavior: A<int> A<int> A<const int *> A<int *> A<void (*)()> A<const char *> A<const wchar_t *> As Thorsten points out, remove_reference has no bearing as T can not be deduced as a reference type in C++03. But fwiw, I can achieve this output with only one make_A overload using the rvalue reference (if now decay has remove_reference). template <class T> inline A<typename decay<T>::type> make_A(T&& t) { return A<typename decay<T>::type>(std::forward<T>(t)); } The single overload: template <class T> inline A<typename decay<T>::type> make_A(T& t) { return A<typename decay<T>::type>(t); } gets nearly a perfect score, handling const qualified objects correctly. Its only failing is that it won't bind to rvalues. The const T& overload by itself doesn't work either because it gets arrays wrong (turns them into const T*). This exposes a weakness in the overload strategy: It doesn't scale well. Consider John's suggested make_pair: make_pair( const F& f, const S& s ); make_pair( F& f, S& s ); This fails with: int a4[4] = {}; std::cout << typeid(std::make_pair(0, a4)).name() << '\n'; std::pair<int, const int *> intead of: std::pair<int, int *> You really need four make_pair overloads to cover everything: make_pair( const F& f, const S& s ); make_pair( const F& f, S& s ); make_pair( F& f, const S& s ); make_pair( F& f, S& s ); Now you should get the correct: std::pair<int, int *> In contrast, only one make_pair is required when using rvalue_references: make_pair( F&& f, S&& s ); -Howard

"Howard Hinnant" <hinnant@twcny.rr.com> wrote in message news:99BAA38A-5E89-11D9-B3F9-003065D18932@twcny.rr.com... | Rambling a little bit today... I set up a little test: | This prints out (on my system): | | A<int> | A<int> | A<const int *> | A<int *> | A<void (*)()> | A<const char *> | A<const wchar_t *> | A<int> | A<volatile int> | A<int[3]> | A<int[4]> | A<void ()> | A<char[7]> | A<wchar_t[5]> I like this behavior. | Using a decay that supports function->function pointer, and one that | strips top level cv-qualifiers off of non-arrays, and non-functions, | and using John's overload strategy: | I can get the original behavior: | | A<int> | A<int> | A<const int *> | A<int *> | A<void (*)()> | A<const char *> | A<const wchar_t *> | You really need four make_pair overloads to cover everything: | | make_pair( const F& f, const S& s ); | make_pair( const F& f, S& s ); | make_pair( F& f, const S& s ); | make_pair( F& f, S& s ); | | Now you should get the correct: And this is really bad. I can't provide all these overloads in boost.assign for 2-5 arguements, say. For me the parameter type must be const T& and in this setting I particulilar need to handle string literals. Function pointers would be nice to handle too (as they currently are), but I feel normal non-const array decay is much less used. So I think the imperfect version in boost and C++03 should just add the const to arrays. -Thorsten

Sure you can. That's only 16 overloads. Use the preprocessor. -- Dave Abrahams Boost Consulting http://www.boost-consulting.com

"David Abrahams" <dave@boost-consulting.com> wrote in message news:crf6sd$m1t$1@sea.gmane.org... | | > And this is really bad. I can't provide all these overloads in boost.assign | > for | > 2-5 arguements, say. | | Sure you can. That's only 16 overloads. Use the preprocessor. AFAICT, up to 5 arguments would give 32 + 16 + 8 + 4 + 2 = 62 overloads. That's a lot of code to parse in a header. How much will this affect compilation time? Given that the usage is minimal in the boost.assign I'm not so keen about doing this currently. -Thorsten .

Thorsten Ottosen wrote:
You're right; what was I thinking?
That's a lot of code to parse in a header. How much will this affect compilation time?
Not much, based on what I've seen in the past, but you'll never know until you try it.
Given that the usage is minimal in the boost.assign I'm not so keen about doing this currently.
It's your party. -- Dave Abrahams Boost Consulting http://www.boost-consulting.com
participants (6)
-
David Abrahams
-
Howard Hinnant
-
John Maddock
-
Jonathan Turkanis
-
Pavel Vozenilek
-
Thorsten Ottosen