Frage: gibt es dafür (benannte?) Quasi-Standards/Pattern, damit ich
nicht lange erklären muss, was ich mir dabei gedacht habe?
Helmut
======== Codebeispiel ==========
struct disallowed {};
struct allowed
{
typedef int allowed_t;
};
template<typename T> class mytraits: public disallowed {};
template<> class mytraits<double>: public allowed {};
class myclass
{
public:
template<typename T> operator T()
{
typedef typename mytraits<T>::allowed_t allowed_t;
T result=0;
return result;
}
private:
};
static myclass d;
static double c1=d; // OK
static int c2=d; // Compile fails
Das ist so eine Art "statische Assertion". Du kannst es aber
vielleicht noch kompakter und leserlicher aufschreiben, indem Du
Boost.TypeTraits, Boost.StaticAssert und/oder
Boost.ConceptCheckLibrary benutzt. Beispiel:
template<typename T> operator T()
{
BOOST_STATIC_ASSERT(
(boost::is_same<T,double>::value) );
T result=0;
return result;
}
Natürlich macht es weniger Sinn, für nur genau einen Typen überhaupt
ein Template zu erstellen. Die Sache mit einer statischen Assertion
hat auch einen Haken: operator T() für ein belibiges T ungleich double
kann immer noch Teil des "overload sets" sein, so dass in einem Fall
wie diesen:
void foo(void*);
void foo(double);
void bar(myclass & o) {
foo(o); // Mehrdeutig. Welches foo?
}
der Aufruf mehrdeutig ist, obwohl sich nur der Operator für T=double
fehlerfrei instanziieren lässt. Einen solchen Fall kann man eigentlich
mit "SFINAE" gut lösen. Allerdings ist SFINAE bei
Konvertierungsoperatoren nicht anwendbar. Zumindest wüsste ich nicht,
wie. Du findest zu dem Thema wahrscheinlich relativ schnell
Informationen, wenn Du nach "SFINAE" und "enable_if" suchst.
Gruß,
SG
boost::enable_if
http://www.boost.org/doc/libs/1_40_0/libs/utility/enable_if.html
Raymond
> boost::enable_ifhttp://www.boost.org/doc/libs/1_40_0/libs/utility/enable_if.html
Danke, das ist das, wonach ich gefragt habe, aber siehe auch meine
Antwort an SG.
Helmut
> Das ist so eine Art "statische Assertion". Du kannst es aber
> vielleicht noch kompakter und leserlicher aufschreiben, indem Du
> Boost.TypeTraits, Boost.StaticAssert und/oder
> Boost.ConceptCheckLibrary benutzt. Beispiel:
>
> template<typename T> operator T()
> {
> BOOST_STATIC_ASSERT(
> (boost::is_same<T,double>::value) );
> T result=0;
> return result;
> }
Ja, so in der Art habe ich es gemeint.
> Natürlich macht es weniger Sinn, für nur genau einen Typen überhaupt
> ein Template zu erstellen.
Klar, ich denke da an Anwendungen wie z.B. dass nur Konvertierung zu
Gleitkommazahlen erlaubt sein soll.
> Die Sache mit einer statischen Assertion
> hat auch einen Haken: operator T() für ein belibiges T ungleich double
> kann immer noch Teil des "overload sets" sein, so dass in einem Fall
> wie diesen:
>
> void foo(void*);
> void foo(double);
>
> void bar(myclass & o) {
> foo(o); // Mehrdeutig. Welches foo?
> }
>
> der Aufruf mehrdeutig ist, obwohl sich nur der Operator für T=double
> fehlerfrei instanziieren lässt.
Das will ich eigentlich auch vermeiden.
> Einen solchen Fall kann man eigentlich
> mit "SFINAE" gut lösen. Allerdings ist SFINAE bei
> Konvertierungsoperatoren nicht anwendbar. Zumindest wüsste ich nicht,
> wie.
:-(
Dann bleibt wohl doch nur, die Interfaces explizit zu spezifizieren
und die Templates nur für die Implementierung zu nehmen, z.B.
operator double(){return convert_to<double>(*this);}
Helmut
template<typename T, bool isExact> class Convertor;
template<typename T> class Convertor<T, false>
{
public:
T operator() (const YourType& source) const
{
// code for converting a YourType to a float/double
}
};
template<typename T> void convert(const YourType& source, T& target)
{
MyConvertor<T, numeric_limits<T>::is_exact> convertor;
target = convertor (source);
}
- Scooser
Danke, ja. Wenn ich eine eigene Template Funktion verwende, duerfte
das mit SFINAE machbar sein.
Eigentlich will ich aber mit dem "normalen" Conversion Operator
auskommen.
Helmut
Es gibt aber einen leichten Trost. In C++0x wird es möglich sein.
#include <type_traits>
#define REQUIRES(...) ,class=typename \
std::enable_if<(__VA_ARGS__)>::type
class foo {
int rep;
public:
foo(int x) : rep(x) {}
template<typename T
REQURIES( std::is_convertible<int,T>::value )
>
operator T() const {
return rep;
}
};
Gruß,
SG
> template<typename T
> REQURIES( std::is_convertible<int,T>::value )
^^^^^^^
kannst Du weglassen
Nicht, dass ich wüsste. std::enable_if erwartet als ersten Template-
Parameter einen boolschen Wert. Es ist also mit boost::enable_if_c
vergleichbar. Man kann natürlich das Makro anders definieren, zB so
template<bool... B> struct and_c : std::true_type {};
template<bool... B> struct and_c<true,B...> : and_c<B...> {};
template<bool... B> struct and_c<false,B...> : std::false_type{};
template<class... Cond> struct and_ : and_c<Cond::value...> {};
template<bool... B> struct or_c : std::false_type {};
template<bool... B> struct or_c<true,B...> : std::true_type{};
template<bool... B> struct or_c<false,B...> : or_c<B...>{};
template<class... Cond> struct or_ : or_c<Cond::value...> {};
template<class Cond> struct not_
: std::integral_constant<bool,(! Cond::value)> {};
#define REQUIRES(...) ,class=typename \
std::enable_if<and_<(__VA_ARGS__)>::value>::type
Allerdings ist die Version nur von Vorteil, wenn man Bedingungen durch
UND Verknüpfen will. Wenn man ODER-Verknüpfung und Negation braucht,
wir es schnell unleserlich:
template<typename T, typename U
REQUIRES( is_convertible<T,int> ,
or_< is_same<U,double>, is_same<U,float> > )
>
// bedeutet:
// is_convertible<T,int>::value &&
// (is_same<U,double>::value || is_same<U,float>::value)
Gruß,
SG
> Es gibt aber einen leichten Trost. In C++0x wird es möglich sein.
>
> #include <type_traits>
>
> #define REQUIRES(...) ,class=typename \
> std::enable_if<(__VA_ARGS__)>::type
>
> class foo {
> int rep;
> public:
> foo(int x) : rep(x) {}
>
> template<typename T
> REQURIES( std::is_convertible<int,T>::value )
> >
> operator T() const {
> return rep;
> }
> };
Funktioniert das intern nicht mit SFINAE (und versagt somit beim
Conversion-Operator)?
Helmut
Richtig.
> (und versagt somit beim
> Conversion-Operator)?
Nein. Es gibt keine Regel, die besagt, dass "SFINAE" bei
Konvertierungsoperatoren verboten sei. Bei C++03 lässt sich SFINAE
schlicht nicht anwenden, weil man das enable_if nirgends einsetzen
könnte -- weder als Teil des return-Typs noch als Typ eines Default-
Parameters. C++0x unterstützt aber Default-Template-Parameter für
Funktionstemplates. Damit sollte es gehen. Das enable_if taucht hier
nämlich als Teil des Default-Template-Parameters auf.
Gruß,
SG
Ich habe an boost gedacht.
> C++0x unterstützt aber Default-Template-Parameter für
> Funktionstemplates. Damit sollte es gehen. Das enable_if taucht hier
> nämlich als Teil des Default-Template-Parameters auf.
Hab's jetzt verstanden, danke
Helmut