SFINAE arbeitet im Rückgabetyp, aber nicht als Vorlagenparameter

Lesezeit: 2 Minuten

SFINAE arbeitet im Ruckgabetyp aber nicht als Vorlagenparameter
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.

  • Danke sehr. Diese Erklärung ist zumindest einfach und klar. Ich hatte keine Ahnung von dieser Regel 🙂

    – Morwenn

    15. März 2013 um 8:55 Uhr

  • Ich liebe diese Aussage: “Standardvorlagenargumente werden nicht berücksichtigt” <- Ich kann jedoch kein Wort finden default auf Seite 34 von open-std.org/jtc1/sc22/wg21/docs/papers/2014/n4205.pdf (Regel 14.5.6.1). Es gibt eine solche Aussage in “de.cppreference.com/w/cpp/language/…“Aber. Würden Sie mir bitte sagen, woher die Interpretation kommt? Danke.

    – cppAnfänger

    26. Februar 2019 um 11:14 Uhr


  • @cppBeginner Nun, ich denke, es ist mehr oder weniger, genau weil es in Äquivalenzdefinitionen keinen Verweis auf Standardparameter gibt. All dies stammte jedoch aus dem C++ 11-Standard, nicht aus einer Art „Referenz“-Site. Wenn Sie an C ++ 14 interessiert sind, ist es meiner Meinung nach besser, sich zumindest den endgültigen Entwurf anzusehen (der ebenfalls kostenlos ist), anstatt einen Vorschlag zu machen, dem viele relevante Teile fehlen.

    – Konstantin Osnobihin

    5. März 2019 um 13:52 Uhr

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";
}

  • Extra : typename = typename... bedeutet Standardargument und kann nur einmal vorhanden sein. Während if durch ersetzen typename...es bedeutet zu SFINAE.

    – heLomaN

    31. Juli 2019 um 9:09 Uhr

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{});
}

  • Dies ist nicht sehr allgemein – es ist spezifisch für diesen genauen Fall – Integer vs. Floating.

    – einpoklum

    20. November 2015 um 1:13 Uhr

988660cookie-checkSFINAE arbeitet im Rückgabetyp, aber nicht als Vorlagenparameter

This website is using cookies to improve the user-friendliness. You agree by using the website further.

Privacy policy