Konstruktor für spezielle Typen "Umleiten"?
-
Aber das funktioniert doch Oo
class C { public: C(int i, long l){} template <class Iter> C(Iter first, Iter last){} }; int main() { int a, b; C c(a,b); //funktioniert }Oder verstehe ich das Problem falsch

-
Ist explicit machen und auf long casten keine Option? Oder einen Konstruktor int,int der ein Objekt erstellt und dann nen swap macht?
-
söfgjsä schrieb:
Oder verstehe ich das Problem falsch

Ja oder du merkst nicht, dass es die falsche Version aufruft. Es ruft nämlich die Templateversion auf. Mach mal das:
#include <iostream> class C { public: C(int i, long l) { std::cout << "int i, long l" << std::endl; } template <class Iter> C(Iter first, Iter last) { std::cout << "Iter first, Iter last" << std::endl; } }; int main() { int a, b; C c(a,b); // Gibt garantiert "Iter first, Iter last" aus. // Und das will er ja gerade nicht ;) }@hustbaer,
Und wie stellst du dir das vor?
Es gibt nicht mal eine eindeutige Möglichkeit einen Iterator zu erkennen ...Ich bin immer noch für einen Konstruktor mit
int, int. Um doppelten Intialisierungslisten Code zu verhindern, kann man die Member intern in einstructverpacken. Zudem kann man eine privateinitFunktion verwenden. Also kurz skizziert:class C { private: struct { int m_i; long m_l; Members(int i, long l) : m_i(i) , m_l(l) { } } m_members; public: C(int i, long l) : m_members(i, l) { init(); } C(int i, int b) : m_members(i, b) { init(); } template <class Iter> C(Iter first, Iter last) : m_members(0, 0) { // whatever ... } private: void init() { // whatever ... } };Kommt natürlich ganz darauf an, wie die Klasse intern aussieht.
Grüssli
-
hustbaer schrieb:
Hm. Ginge das nicht mit boost::enable_if?
Ja, an SFINAE hatte ich auch gedacht. Es fehlt nur noch eine Abfrage, ob "Iter" wirklich ein Iterator ist.
class A { public: A(int,long); template<typename Iter> A(Iter beg, Iter end, typename boost::enable_if<is_iterator<Iter>,char>::type=0) { //... } };Leider fällt mir spontan nichts für
is_iteratorein. Mit Concepts wär das einfach gewesen (requiresInputIterator<Iter>).Gruß,
SP
-
Nachtrag:
Es gibt eine Diskussion is_iterator in der Boost Mailingliste. Es sieht nicht so aus, als ob es in C++03 möglich wäre.Gruß,
SP
-
SFINAE in C++0x mit Default-Template-Argumenten für Funktionen und decltype:
template<typename T> struct make { static T& lvalue(); static T&& rvalue(); }; class A { public: A(int,long); template< typename Iter, // Dereferenzieren möglich? typename Test = decltype(*make<Iter>::rvalue()) > // ^ A(Iter beg, Iter end) { // ... } };müsste nach meinem Verständnis in C++0x funktionieren. Diese oder ähnliche Varianten habe ich aber unter g++ 4.3 mit -std=c++0x nicht zum Laufen bekommen, da der Compiler es vorzieht, eine Fehlermeldung auszuspucken, statt das Template einfach zu ignorieren falls das Einsetzen schiefgeht. Ihr könnt ja mal mit g++ 4.4 testen -- der soll, was SFINAE angeht, gnädiger sein.
Mit diesen "erweiterten SFINAE Möglichkeiten" wären wir zumindest nicht ganz verloren ... so ohne Concepts. Die ganzen syntaktischen Typ-Bedingungen sollten per decltype testbar sein.

Gruß,
SP
-
Sebastian Pizer schrieb:
template<typename Iter> A(Iter beg, Iter end, typename boost::enable_if<is_iterator<Iter>,char>::type=0)Guter Ansatz, ich werds mal mit mit
boost::disable_if<is_integral<Iter>,char>::type = 0versuchen wenn ich das nächstemal dran sitze
-
pumuckl schrieb:
Guter Ansatz, ich werds mal mit mit
boost::disable_if<is_integral<Iter>,char>::type = 0versuchen wenn ich das nächstemal dran sitze
So geht's natürlich auch!

Oder vielleicht sogar mitis_convertible<Iter,int>undis_convertible<Iter,long>.Ich habe übrigends vorhin gcc-4.4.1 kompiliert, um das C++0x-Beispiel mit decltype zu testen: Es funzt.

Gruß,
SP
-
War leider nichts - er versucht krampfhaft die Iteratorversion aufzurufen bei zwei ints und stolpert über enable_if::type

-
pumuckl schrieb:
War leider nichts - er versucht krampfhaft die Iteratorversion aufzurufen bei zwei ints und stolpert über enable_if::type

Kein Wunder, es ist ja auch kein Substitutionsfehler. Die Substitution klappt einwandfrei. Grundsätzlich müsstest du sowas machen:
template<typename IterT> C(IterT, IterT, typename std::iterator_traits<IterT>::iterator_category category = typename std::iterator_traits<IterT>::iterator_category()) { }Nur dass es problematisch ist, weil es unter MSVC zum Beispiel auch Spezialisierungen für
std::iterator_traitsfürint,double,float,long,short, usw. gibt. Daher funktioniert dies auch nicht ... Es gibt eben keine Möglichkeit einen Iterator zu erkennen.Das einzige was du machen könntest, sind gewisse Typen explizit auszuschliessen. Ehm, das müsste dann so gehen:
struct EmptyType { }; temlate<typename T> struct ExcludedTraits { typename EmptyType Allowed; }; template<> struct ExcludedTraits<int> { // int ausschliessen. }; class C { public: C(int, long) { std::cout << "funzt" << std::endl; } template<typename IterT> C(IterT, IterT, typename ExcludedTraits<IterT>::Allowed = typename ExcludedTraits<IterT>::Allowed()) { std::cout << "meep" << std::endl; } }; int main() { int a = 0; int b = 0; C c(a, b); // sollte gehen ... }Vielleicht kann man es über Typlisten noch ein wenig vereinfachen.
Aber ob das viel einfacher ist als einfach einen zusätzlichen Konstruktor?
Grüssli
-
pumuckl schrieb:
War leider nichts - er versucht krampfhaft die Iteratorversion aufzurufen bei zwei ints und stolpert über enable_if::type

Wie wär's mit etwas Code? Ich habe vorhin das hier probiert:
#include <iostream> #include <boost/utility/enable_if.hpp> #include <boost/type_traits/is_integral.hpp> void foo(int, long) { std::cout << "1\n"; } template<typename Iter> void foo(Iter beg, Iter end, typename boost::disable_if<boost::is_integral<Iter>,char>::type=0) { std::cout << "2\n"; } int main() { foo(23,42); }und es macht genau das, was es soll ("1" ausgeben).
Gruß,
SP
-
Hätte ein normaler User so eine Frage gestellt, wäre schon längst die Frage gekommen: Was willst du eigentlich konkret damit erreichen? Da du aber sicher weiß, was du willst, ist die Farge wohl überflüssig. Mich interessiert aber schon, warum man einen Konstruktor benötigt, der ein
intund einlongals Parameter hat. Schließlich gilt immersizeof(int) <= sizeof(long), also könntest du auch zwei long als Parameter nehmen.Gruß
Don06
-
Oh Gott, ich trottel ... klar funktioniert dies! Ich habe
boost::enable_if<cond, char>mitSelect<cond, char, void>verwechselt.Ich ziehe mein vorheriges Posting zurück ... peinlich, peinlich ...
Grüssli
-
Ich bin dabei ein Containertemplate zu schreiben. Wie bei std::vector solls einen Ctor für ein iterator-Range geben, sowie einen für size_type und T. letzterem kann man bei std::vector für T=int oder T=long usw. zwei int übergeben, bzw. sollte man. In der Standardbibliothek die ich hier habe ist es so gelöst, dass der Range-Ctor bei Iter=int sich so verhält wie der NxT-Ctor. Ich wollte nach eier Lösung suchen wo gleich der richtige Ctor gewählt wird.
Mein Ansatz bisher:
template <class T> class MyContainer /*: boost::equality_comparable<MyContainer<T> >*/ { template <class Iter> struct DisableInt { typedef typename boost::disable_if< boost::is_integral<Iter>, char >::type type; }; public: MyContainer(); explicit MyContainer( size_type n, const_value_type& value= T()); template <class InputIterator > MyContainer(InputIterator first, InputIterator last, typename DisableInt<InputIterator>::type = 0); };Beim Compilieren gibts dann entsprechend eine Fehlermeldung:
c:\development\projects\lib_pumu\src\pumu\container\mycontainer.hpp(26) : error C2039: 'type' : is not a member of 'boost::disable_if<Cond,T>' 1> with 1> [ 1> Cond=boost::is_integral<int>, 1> T=char 1> ] 1> c:\development\projects\lib_pumu\test\pumu\container\testmycontainer.cpp(81) : see reference to class template instantiation 'pumu::container::MyContainer<T>::DisableInt<Iter>' being compiled 1> with 1> [ 1> T=int, 1> Iter=int 1> ]
-
Wirf das
struct DisableIntweg. So wird SFINAE nicht funktionieren. Der Substitutionsfehler passiert nicht im Funktionskopf, sondern in der StrukturDisableInt. Der Fehler muss aber im Funktionskopf passieren, damit es funktioniert.Grüssli
-
Ja hatte da n Denkfehler. Hiermit gehts:
template <class Iter> struct DisableInt : public boost::disable_if<boost::is_integral<Iter>, char> {};
-
Dravere schrieb:
Nur dass es problematisch ist, weil es unter MSVC zum Beispiel auch Spezialisierungen für
std::iterator_traitsfürint,double,float,long,short, usw. gibt. Daher funktioniert dies auch nicht ... Es gibt eben keine Möglichkeit einen Iterator zu erkennen.Das ist komisch. Weisst du wozu es diese Spezialisierungen gibt?
Aber mal davon abgesehen... könnte man sich nicht zu nutze machen dass in dem Fall
Iterundstd::iterator_traits<Iter>::value_typeder selbe Typ sind?Ich muss sagen, als ich enable_if vorgeschlagen habe, bin ich einfach davon ausgegangen, dass es recht einfach möglich sein muss
is_iteratorzu implementieren, da es jastd::iterator_traitsgibt.Irgendwie ziemlich doof dass es doch nicht so einfach ist

-
hustbaer schrieb:
Irgendwie ziemlich doof dass es doch nicht so einfach ist

Ich schätze mal wenns so einfach wäre gäbs bei boost auch ein is_iterator oder ähnliches. Es gibt in der Mailing-List schon eine längere Diskussion zu dem Thema:
-
hustbaer schrieb:
Das ist komisch. Weisst du wozu es diese Spezialisierungen gibt?
Nicht die Bohne einer Ahnung! Es steht nicht mal hier etwas:
http://msdn.microsoft.com/en-us/library/zdxb97eh.aspxIch weiss es selber nur, weil ich mal etwas über
std::iterator_traitslösen wollte und dabei voll auf die Schnauze gefallen bin. Vor allem habe ich zuerst ziemlich lange gesucht, bis ich gemerkt habe, dass da zusätzliche Spezialisierungen vorhanden sind. Ich wäre niemals auf diese Idee gekommen
Hey ... moment mal ... würde sowas nicht gehen?
template<typename CategoryT> struct isValidIteratorCategory { enum ( value = 0 }; }; template<> struct isValidIteratorCategory<std::input_iterator_tag> { enum { value = 1; }; }; template<> struct isValidIteratorCategory<std::output_iterator_tag> { enum { value = 1; }; }; template<> struct isValidIteratorCategory<std::forward_iterator_tag> { enum { value = 1; }; }; template<> struct isValidIteratorCategory<std::bidirectional_iterator_tag> { enum { value = 1; }; }; template<> struct isValidIteratorCategory<std::random_access_iterator_tag> { enum { value = 1; }; }; template<typename IterT> struct IsIterator { enum { value = isValidIteratorCategory<typename std::iterator_traits<IterT>::iterator_category>::value }; }; template<typename T> struct IsIterator<T*> { enum { value = 1; }; }; template<typename T> struct IsIterator<T const*> { enum { value = 1; } };Oder überseh ich etwas?
Grüssli
-
Dravere schrieb:
Oder überseh ich etwas?
class Alien {}; int main() { cout << IsIterator<Alien>::value; }Das ist dann möglicherweise der Grund, weshalb Spezialisierungen für int&co. eingeführt werden, um nicht schon iterator_traits<int>::iterator_category auszusteigen, sondern ggf. das Ergebnis per sfinae zu verwerten.
vielleicht so
typedef char no; typedef no yes[2]; no& tag_test(...); template <typename T> yes& tag_test(const T&, iterator_traits<T>::iterator_category*=0); template <typename T> struct has_tag : mpl::bool_<sizeof(tag_test(*(T*)0))==sizeof(yes)> {}; typedef mpl::vector<input_iterator_tag,output_iterator_tag,forward_iterator_tag,random_access_iterator_tag> valid_tags; template <typename T, bool = has_tag<T>::value> struct is_iterator : mpl::contains<valid_tags,T> {}; template <typename T> struct is_iterator< T, false> : mpl::bool_<false> {};