
Morwenn
Ich habe das SFINAE-Idiom schon einige Male verwendet und mich daran gewöhnt, mein std::enable_if<>
in Vorlagenparametern und nicht in Rückgabetypen. Ich bin jedoch auf einen trivialen Fall gestoßen, in dem es nicht funktioniert hat, und ich bin mir nicht sicher, warum. Hier erstmal mein Main:
int main()
{
foo(5);
foo(3.4);
}
Hier ist eine Implementierung von foo
das löst den Fehler aus:
template<typename T,
typename = typename std::enable_if<std::is_integral<T>::value>::type>
auto foo(T)
-> void
{
std::cout << "I'm an integer!\n";
}
template<typename T,
typename = typename std::enable_if<std::is_floating_point<T>::value>::type>
auto foo(T)
-> void
{
std::cout << "I'm a floating point number!\n";
}
Und hier ist ein angeblich äquivalenter Code, der gut funktioniert:
template<typename T>
auto foo(T)
-> typename std::enable_if<std::is_integral<T>::value>::type
{
std::cout << "I'm an integrer!\n";
}
template<typename T>
auto foo(T)
-> typename std::enable_if<std::is_floating_point<T>::value>::type
{
std::cout << "I'm a floating point number!\n";
}
Meine Frage ist: warum funktioniert die erste Implementierung von foo
löst diesen Fehler aus, während der zweite ihn nicht auslöst?
main.cpp:14:6: error: redefinition of 'template<class T, class> void foo(T)'
auto foo(T)
^
main.cpp:6:6: note: 'template<class T, class> void foo(T)' previously declared here
auto foo(T)
^
main.cpp: In function 'int main()':
main.cpp:23:12: error: no matching function for call to 'foo(double)'
foo(3.4);
^
main.cpp:6:6: note: candidate: template<class T, class> void foo(T)
auto foo(T)
^
main.cpp:6:6: note: template argument deduction/substitution failed:
main.cpp:5:10: error: no type named 'type' in 'struct std::enable_if<false, void>'
typename = typename std::enable_if<std::is_integral<T>::value>::type>
^
BEARBEITEN :
Arbeitscode und fehlerhafter Code.
Sollte man sich anschauen 14.5.6.1 Function template overloading
(C++11-Standard), wo die Äquivalenz von Funktionsvorlagen definiert ist. Kurz gesagt, Standardvorlagenargumente werden nicht berücksichtigt, sodass Sie im ersten Fall dieselbe Funktionsvorlage zweimal definiert haben. Im 2. Fall haben Sie ausdrucksbezogene Vorlagenparameter, die im Rückgabetyp verwendet werden (siehe erneut 14.5.6.1/4). Da dieser Ausdruck Teil der Signatur ist, erhalten Sie zwei verschiedene Funktionsvorlagen-Deklarationen und somit erhält SFINAE die Möglichkeit zu arbeiten.
Werte in den Vorlagen funktionieren:
template<typename T,
typename std::enable_if<std::is_integral<T>::value, int>::type = 0>
auto foo(T)
-> void
{
std::cout << "I'm an integer!\n";
}
template<typename T,
typename std::enable_if<std::is_floating_point<T>::value, int>::type = 0>
auto foo(T)
-> void
{
std::cout << "I'm a floating point number!\n";
}
Die = ...
der Vorlage gibt nur einen Standardparameter an. Dies ist nicht Teil der eigentlichen Signatur, wie sie aussieht
template<typename T, typename>
auto foo(T a);
zum beide Funktionen.
Abhängig von Ihren Anforderungen ist die allgemeinste Lösung für dieses Problem die Verwendung von Tag-Dispatching.
struct integral_tag { typedef integral_tag category; };
struct floating_tag { typedef floating_tag category; };
template <typename T> struct foo_tag
: std::conditional<std::is_integral<T>::value, integral_tag,
typename std::conditional<std::is_floating_point<T>::value, floating_tag,
std::false_type>::type>::type {};
template<typename T>
T foo_impl(T a, integral_tag) { return a; }
template<typename T>
T foo_impl(T a, floating_tag) { return a; }
template <typename T>
T foo(T a)
{
static_assert(!std::is_base_of<std::false_type, foo_tag<T> >::value,
"T must be either floating point or integral");
return foo_impl(a, typename foo_tag<T>::category{});
}
struct bigint {};
template<> struct foo_tag<bigint> : integral_tag {};
int main()
{
//foo("x"); // produces a nice error message
foo(1);
foo(1.5);
foo(bigint{});
}
9886600cookie-checkSFINAE arbeitet im Rückgabetyp, aber nicht als Vorlagenparameteryes
OK. Aktuelle Vorführung: 1. Teil konnte nicht kompiliert werden und 2. erfolgreich zusammengestellter Teil.
– Markus García
15. März 2013 um 8:34 Uhr
Zusätzliche Informationen: gleiche mit VS 2012 November CTP.
– Markus García
15. März 2013 um 8:45 Uhr