2016-08-02 17 views
4

Das Buch von Stroustrup gibt ein Beispiel zur Beantwortung der Frage: "Ist es möglich, vom Typ X aufzurufen" (Abschnitt 28.4.4 "Weitere Beispiele mit Aktivieren_if "). Ich habe versucht, das Beispiel zu reproduzieren, habe aber etwas falsch gemacht und kann nicht verstehen, was.Test, ob der Aufruf von f (x) mit Metaprogrammierung möglich ist

In meinem Code unten gibt es eine Funktion f(int). Ich erwarte, dass dann das Ergebnis von has_f<int>::value1 (true) ist. Das tatsächliche Ergebnis ist 0 (false).

#include <type_traits> 
#include <iostream> 

// 
// Meta if/then/else specialization 
// 
struct substitution_failure { }; 

template<typename T> 
struct substitution_succeeded : std::true_type { }; 

template<> 
struct substitution_succeeded<substitution_failure> : std::false_type { }; 

// 
// sfinae to derive the specialization 
// 
template<typename T> 
struct get_f_result { 
private: 
    template<typename X> 
    static auto check(X const& x) -> decltype(f(x)); 
    static substitution_failure check(...); 
public: 
    using type = decltype(check(std::declval<T>())); 
}; 

// 
// has_f uses the derived specialization 
// 
template<typename T> 
struct has_f : substitution_succeeded<typename get_f_result<T>::type> { }; 

// 
// We will check if this function call be called, 
// once with "char*" and once with "int". 
// 
int f(int i) { 
    std::cout << i; 
    return i; 
} 

int main() { 
    auto b1{has_f<char*>::value}; 
    std::cout << "test(char*) gives: " << b1 << std::endl; 
    std::cout << "Just to make sure we can call f(int): "; 
    f(777); 
    std::cout << std::endl; 
    auto b2{has_f<int>::value}; 
    std::cout << "test(int) gives: " << b2 << std::endl; 
} 

Der Ausgang:

test(char*) gives: 0 
Just to make sure we can call f(int): 777 
test(int) gives: 0 

Antwort

5

Das Hauptproblem ist, dass Sie einen uneingeschränkten Anruf f hier machen:

template<typename X> 
static auto check(X const& x) -> decltype(f(x)); 

Die f s, die an der Stelle der Definition von check() (keine) in Rahmen gefunden werden diejenigen sein wird und diejenigen, die durch argumentabhängige Suche in den zugehörigen Namespaces von X gefunden werden. Seit X ist int, es hat keine zugeordneten Namespaces, und Sie finden keine f s entweder. Da ADL nie für int arbeitet, muss Ihre Funktion sichtbar sein, bevor get_f_result definiert ist. Wenn man es einfach hochzieht, löst man dieses Problem.


Jetzt ist Ihre has_f übermäßig kompliziert. Es gibt keinen Grund für die substitution_succeeded Maschinen. Gerade haben die beiden check() Überlastungen Sie den gewünschten Typ zurück:

template<typename T> 
struct has_f { 
private: 
    template <typename X> 
    static auto check(X const& x) 
     -> decltype(f(x), std::true_type{}); 

    static std::false_type check(...); 
public: 
    using type = decltype(check(std::declval<T>())); 
}; 

Und jetzt has_f<T>::type ist bereits entweder true_type oder false_type.


Natürlich ist auch das zu kompliziert. Prüfen, ob ein Ausdruck gültig ist, wird eine ziemlich häufige Operation, so dass es nützlich sein würde, es zu vereinfachen (entlehnt aus Yakk, ähnlich wie std::is_detected):

namespace impl { 
    template <template <class...> class, class, class... > 
    struct can_apply : std::false_type { }; 

    template <template <class...> class Z, class... Ts> 
    struct can_apply<Z, std::void_t<Z<Ts...>>, Ts...> : std::true_type { }; 
}; 

template <template <class... > class Z, class... Ts> 
using can_apply = impl::can_apply<Z, void, Ts...>; 

dieses Sie Lassen Sie uns schreiben:

+0

Bah, 'is_detected' ist in Bibliothek Fundamentals v2 und nicht alle der Bibliothek Fundamentals ** v1 ** in C++ 17. :( – Yakk

2

kann ich 2 Möglichkeiten sehen das Problem zu beheben Sie sehen:

  1. Vorwärts Ihre Funktion f erklären. Dies ist erforderlich, weil Sie die Funktion explizit über den Namen in der Vorlage get_f_result aufrufen.

int f(int); template<typename T> struct get_f_result { private: template<typename X> static auto check(X const& x) -> decltype(f(x)); static substitution_failure check(...); public: using type = decltype(check(std::declval<T>())); };

  1. Die zweite Lösung ist, um es generic also nicht nur für f(c) sondern für die ganze Funktion, die nimmt ein int:
 
// 
// sfinae to derive the specialization 
// 
template <typename Func, Func f, typename T> 
struct get_f_result { 
private: 
    template <typename X> 
    static auto check(X const& x) -> decltype(f(x)); 
    static substitution_failure check(...); 
public: 
    using type = decltype(check(std::declval<T>())); 
}; 

Und rufen Sie es wie:

Wieder hier f muss hier bekannt sein..aber, Sie können es wieder generischer machen, indem Sie die Verantwortung für die Bereitstellung der Funktion an der Anruferseite verschieben.

+0

Would macht es Ihnen etwas aus, auf 1. näher einzugehen? Ich denke, das naive Denken ist, dass die Deklaration von "f" nicht vor der Instantiierung des Templates benötigt wird, was in "main" gemacht wird, wobei "f (int)" deklariert wird. Es sieht so aus, als ob die Vorlage "weiß", dass es fehlschlägt. – luk32

+0

@Arunmu 'f' ist ein abhängiger Name. – Barry

+0

@Barry Ah..yes Ich werde immer verwirrt. In diesem Fall funktioniert es so, wie Sie es mit ADL und Nicht-ADL-Lookup erklärt haben. – Arunmu