Template-Problem (deduction/substitution failed)



  • Das wäre ok, geht bei mir so aber leider nicht. Das konntest du aber nicht wissen, da ich meinen Code zu weit zusammengestutzt hab...

    In Wirklichkeit sieht es so aus:

    template <class U>
    size_t memory(const std::deque<U>& obj) {
        size_t sum = sizeof(obj);
        for (const U& u: obj) {
            sum += memory(u); // hier failt die template deduction
        }
        return sum;
    }
    
    template <class DataContainer, class T, class Weight, class Coreset, class Threshold>
    size_t RangeClustering::OneDTree<DataContainer, T, Weight, Coreset, Threshold>::memoryUsage() const {
        return memory(nodeContainer); // nodeContainer ist vom Typ std::deque<OneDNode>
    }
    


  • Hm, bekomme ich nicht reproduziert. Mach mal ein kompilierbares Minimalbeispiel mit dem gleichen Fehler.



  • Das Problem hat einen anderen Grund:
    Template-Typen können sich nicht aus nested-types deduzieren lassen. Das wäre hier der Fall:

    template <class DataContainer, class T, class Weight, class Coreset, class Threshold> 
    size_t memory(const typename RangeClustering::OneDTree<DataContainer, T, Weight, Coreset, Threshold>::OneDNode& node) { 
        return sizeof(node); 
    }
    

    Also der Compiler kennt ja den Typen, den du an die Funktion übergibst, hier mal U . Aber wie soll er DataContainer, T, Weight, Coreset, Threshold dadurch deduzieren? Das Template für alle existenten Typen instanziieren und schauen, ob dann OneDNode U entspricht (das dürfte kaum in absehbarer Zeit möglich sein)? Und wenn es mehrere Instanziierungen gibt (was sehr wahrscheinlich der Fall ist), schlägt die Deduzierung ohnehin fehl. Aufrufe von memory werden also nie klappen wenn du die Argumente nicht explizit vorgibst.

    Edit:
    Äquivalentes Minimalbeispiel:

    template<typename T>
    struct Foo
    {
    	typedef T Type;
    };
    
    template<typename T>
    void Bar(typename Foo<T>::Type)
    {
    }
    
    int main()
    {
    	Bar(5);
    }
    

    Für uns Menschen ist es natürlich offensichtlich, dass hier T als int deduziert werden sollte, das geht leider selbst bei so einem einfachen Beispiel nicht. Bzw. zum Glück! Alles andere wäre schlecht da es in komplizierteren Fällen nicht funktionieren wird.

    Edit 2:
    Wenn in derselben Instanziierung die Template-Argumente anderweitig deduziert werden können, ist es natürlich kein Problem dass man sie nicht explizit angibt:

    template <class ForwardIterator>
    ForwardIterator next(ForwardIterator it, typename iterator_traits<ForwardIterator>::difference_type n = 1);
    


  • ok, damit weiß ich jetzt auf jeden Fall, woran der Fehler liegt.

    Wie würdet ihr das Problem am Besten umgehen? Die Unterklasse als eigenständgie Klasse implementieren? Oder die Implementierung von memory(OneDTree) so gestalten, dass keine weiteren Funktionen mehr aufgerufen werden? Letzteres würde natürlich zur Code-Dublizierung führen, was nicht so schön ist. Oder gibt es noch eine bessere Alternative?


  • Mod

    Ramanujan schrieb:

    ok, damit weiß ich jetzt auf jeden Fall, woran der Fehler liegt.

    Wie würdet ihr das Problem am Besten umgehen? Die Unterklasse als eigenständgie Klasse implementieren? Oder die Implementierung von memory(OneDTree) so gestalten, dass keine weiteren Funktionen mehr aufgerufen werden? Letzteres würde natürlich zur Code-Dublizierung führen, was nicht so schön ist. Oder gibt es noch eine bessere Alternative?

    Der Compiler kennt den Zusammenhang zwischen OneDNode und OneDTree<DataContainer, T, Weight, Coreset, Threshold> nicht, du dagegen schon. Also kann man nat. nachhelfen. Z.B. dadurch, dass OneDNode ein sein OneDTree<DataContainer, T, Weight, Coreset, Threshold> kennt.
    Ich unterstelle mal, dass OneDNode ein typedef names tree_type auf sein OneDTree<DataContainer, T, Weight, Coreset, Threshold> hat.
    Dann würde z.B. funktionieren

    template <typename T> struct isOneDTree : std::false_type {};
    template <class DataContainer, class T, class Weight, class Coreset, class Threshold>
    struct isOneDTree<RangeClustering::OneDTree<DataContainer, T, Weight, Coreset, Threshold> : std::true_type {};
    
    template <typename Node>
    typename std::enable_if<isOneDtree<typename Node::tree_type, size_t>::type memory(const Node& node) {
        return sizeof(node);
    }
    

    Es gibt verschiedene Möglichkeiten, das zu schreiben; die Grundidee ist, anstatt den Funktionsparameter strukturell einzuschränken, diesen sehr allgemein zu halten (const T& frisst ja alles), und dann aber darauf ein geeignetes Prädikat anzuwenden, das die möglichen Typen einschränkt.



  • Eine interessante Lösung, allerdings empfinde ich die als recht umständlich. Ich hab es nun doch so gelöst, dass ich die innere Klasse zu einer eigenen Klasse gemacht hab. Allerdings kompiliert es immer noch nicht: Er kann das Template scheinbar nicht finden.

    Hier der Fehler-Log:

    In file included from ../RangeClustering/RangeClustering/../Container/ExternalArray.h:10:0,
    from ../RangeClustering/RangeClustering/Test_RangeClustering.cpp:9:
    ../RangeClustering/RangeClustering/../Container/../Memory.h: In instantiation of 'size_t memory(const std::deque<U>&) [with U = RangeClustering::OneDNode<float, unsigned int, std::vector<std::pair<float, unsigned int>, std::allocator<std::pair<float, unsigned int> > >, double>; size_t = unsigned int]':
    ../RangeClustering/RangeClustering/../RangeClustering/OneDTree.h:171:32: required from 'size_t RangeClustering::OneDTree<DataContainer, T, Weight, Coreset, Threshold>::memoryUsage() const [with DataContainer = std::vector<float>; T = float; Weight = unsigned int; Coreset = std::vector<std::pair<float, unsigned int>, std::allocator<std::pair<float, unsigned int> > >; Threshold = double; size_t = unsigned int]'
    ../RangeClustering/RangeClustering/Test_RangeClustering.cpp:359:1: required from here
    ../RangeClustering/RangeClustering/../Container/../Memory.h:47:24: error: no matching function for call to 'memory(const RangeClustering::OneDNode<float, unsigned int, std::vector<std::pair<float, unsigned int>, std::allocator<std::pair<float, unsigned int> > >, double>&)'
    sum += memory(u);
    ^
    ../RangeClustering/RangeClustering/../Container/../Memory.h:47:24: note: candidates are:
    ../RangeClustering/RangeClustering/../Container/../Memory.h:10:70: note: template<class T> typename std::enable_if<std::is_fundamental<_Tp>::value, unsigned int>::type memory(const T&)
    typename std::enable_if<std::is_fundamental<T>::value, size_t>::type memory(const T& obj) {
    ^
    ../RangeClustering/RangeClustering/../Container/../Memory.h:10:70: note: template argument deduction/substitution failed:
    ../RangeClustering/RangeClustering/../Container/../Memory.h: In substitution of 'template<class T> typename std::enable_if<std::is_fundamental<_Tp>::value, unsigned int>::type memory(const T&) [with T = RangeClustering::OneDNode<float, unsigned int, std::vector<std::pair<float, unsigned int>, std::allocator<std::pair<float, unsigned int> > >, double>]':
    ../RangeClustering/RangeClustering/../Container/../Memory.h:47:24: required from 'size_t memory(const std::deque<U>&) [with U = RangeClustering::OneDNode<float, unsigned int, std::vector<std::pair<float, unsigned int>, std::allocator<std::pair<float, unsigned int> > >, double>; size_t = unsigned int]'
    ../RangeClustering/RangeClustering/../RangeClustering/OneDTree.h:171:32: required from 'size_t RangeClustering::OneDTree<DataContainer, T, Weight, Coreset, Threshold>::memoryUsage() const [with DataContainer = std::vector<float>; T = float; Weight = unsigned int; Coreset = std::vector<std::pair<float, unsigned int>, std::allocator<std::pair<float, unsigned int> > >; Threshold = double; size_t = unsigned int]'
    ../RangeClustering/RangeClustering/Test_RangeClustering.cpp:359:1: required from here
    ../RangeClustering/RangeClustering/../Container/../Memory.h:10:70: error: no type named 'type' in 'struct std::enable_if<false, unsigned int>'
    ../RangeClustering/RangeClustering/../Container/../Memory.h: In instantiation of 'size_t memory(const std::deque<U>&) [with U = RangeClustering::OneDNode<float, unsigned int, std::vector<std::pair<float, unsigned int>, std::allocator<std::pair<float, unsigned int> > >, double>; size_t = unsigned int]':
    ../RangeClustering/RangeClustering/../RangeClustering/OneDTree.h:171:32: required from 'size_t RangeClustering::OneDTree<DataContainer, T, Weight, Coreset, Threshold>::memoryUsage() const [with DataContainer = std::vector<float>; T = float; Weight = unsigned int; Coreset = std::vector<std::pair<float, unsigned int>, std::allocator<std::pair<float, unsigned int> > >; Threshold = double; size_t = unsigned int]'
    ../RangeClustering/RangeClustering/Test_RangeClustering.cpp:359:1: required from here
    ../RangeClustering/RangeClustering/../Container/../Memory.h:35:8: note: template<class U> size_t memory(const std::vector<_RealType>&)
    size_t memory(const std::vector<U>& obj) {
    ^
    ../RangeClustering/RangeClustering/../Container/../Memory.h:35:8: note: template argument deduction/substitution failed:
    ../RangeClustering/RangeClustering/../Container/../Memory.h:47:24: note: 'const RangeClustering::OneDNode<float, unsigned int, std::vector<std::pair<float, unsigned int>, std::allocator<std::pair<float, unsigned int> > >, double>' is not derived from 'const std::vector<_RealType>'
    sum += memory(u);
    ^
    ../RangeClustering/RangeClustering/../Container/../Memory.h:44:8: note: template<class U> size_t memory(const std::deque<U>&)
    size_t memory(const std::deque<U>& obj) {
    ^
    ../RangeClustering/RangeClustering/../Container/../Memory.h:44:8: note: template argument deduction/substitution failed:
    ../RangeClustering/RangeClustering/../Container/../Memory.h:47:24: note: 'const RangeClustering::OneDNode<float, unsigned int, std::vector<std::pair<float, unsigned int>, std::allocator<std::pair<float, unsigned int> > >, double>' is not derived from 'const std::deque<U>'
    sum += memory(u);

    In der "Memory.h" steht die Deklaration und Definition der beiden folgenden Funktionstemplates:

    template <class T>
    typename std::enable_if<std::is_fundamental<T>::value, size_t>::type memory(const T& obj) {
        return sizeof(obj);
    }
    
    template <class U>
    size_t memory(const std::deque<U>& obj) {
        size_t sum = sizeof(obj);
        for (const U& u: obj) {
            sum += memory(u);
        }
        return sum;
    }
    

    Im Fehlerlog des Compilers sieht man, dass offensichtlich nur die Funktionstemplates in der "Memory.h" durchprobiert werden

    In der OneDTree.h steht:

    template <class DataContainer, class T, class Weight, class Coreset, class Threshold>
    size_t memory(const RangeClustering::OneDTree<DataContainer, T, Weight, Coreset, Threshold>& tree) {
        return tree.memoryUsage();
    }
    
    template <class T, class Weight, class Coreset, class Threshold>
    size_t memory(const RangeClustering::OneDNode<T, Weight, Coreset, Threshold>& node) {
        return sizeof(node);
    }
    
    template <class DataContainer, class T, class Weight, class Coreset, class Threshold>
    size_t RangeClustering::OneDTree<DataContainer, T, Weight, Coreset, Threshold>::memoryUsage() const {
        return memory(nodeContainer) + sizeof(void*) + sizeof(threshold) + sizeof(root);
    }
    

    nodeContainer ist vom Typ std::deque<OneDNode<...>>

    Kann mir jemand erklären, warum der Compiler das Funktionstemplate in der "OneDTree.h" nicht findet?


  • Mod

    Ist RangeClustering ein Namensraum oder eine Klasse? Falls es ein Namensraum ist, müssen auch die memory-Funktionen in diesem Namensraum liegen, da sie nur per ADL gefunden werden können, und bei diesen Templateparametern std der einzige andere Namensraum ist, der durchsucht wird.



  • Ja, RangeClustering ist ein Namensraum.

    Ich hab es jetzt angepasst, aber es kompiliert immer noch nicht:

    OneDTree.h:

    namespace RangeClustering {
    
    template <class DataContainer, class T, class Weight, class Coreset, class Threshold>
    size_t memory(const OneDTree<DataContainer, T, Weight, Coreset, Threshold>& tree) {
        return tree.memoryUsage();
    }
    
    template <class T, class Weight, class Coreset, class Threshold>
    size_t memory(const OneDNode<T, Weight, Coreset, Threshold>& node) {
        return sizeof(node);
    }
    
    } // namespace
    
    template <class DataContainer, class T, class Weight, class Coreset, class Threshold>
    size_t RangeClustering::OneDTree<DataContainer, T, Weight, Coreset, Threshold>::memoryUsage() const {
        return memory(nodeContainer) + sizeof(void*) + sizeof(threshold) + sizeof(root); // <---- Der Fehler ist hier
    }
    

    Jetzt findet er memory(std::deque<U>) nicht mehr. Die Funktion memory(std::deque<U>) liegt allerdings nicht im Namespace RangeClustering. Ist das das Problem?

    Fehlermeldung:

    In file included from ../RangeClustering/RangeClustering/Test_RangeClustering.cpp:15:0:
    ../RangeClustering/RangeClustering/../RangeClustering/OneDTree.h: In instantiation of 'size_t RangeClustering::OneDTree<DataContainer, T, Weight, Coreset, Threshold>::memoryUsage() const [with DataContainer = std::vector<float>; T = float; Weight = unsigned int; Coreset = std::vector<std::pair<float, unsigned int>, std::allocator<std::pair<float, unsigned int> > >; Threshold = double; size_t = unsigned int]':
    ../RangeClustering/RangeClustering/Test_RangeClustering.cpp:359:1: required from here
    ../RangeClustering/RangeClustering/../RangeClustering/OneDTree.h:173:32: error: no matching function for call to 'memory(const std::deque<RangeClustering::OneDNode<float, unsigned int, std::vector<std::pair<float, unsigned int>, std::allocator<std::pair<float, unsigned int> > >, double>, std::allocator<RangeClustering::OneDNode<float, unsigned int, std::vector<std::pair<float, unsigned int>, std::allocator<std::pair<float, unsigned int> > >, double> > >&)'
    return memory(nodeContainer) + sizeof(void*) + sizeof(threshold) + sizeof(root);
    ^
    ../RangeClustering/RangeClustering/../RangeClustering/OneDTree.h:173:32: note: candidates are:
    ../RangeClustering/RangeClustering/../RangeClustering/OneDTree.h:157:8: note: template<class DataContainer, class T, class Weight, class Coreset, class Threshold> size_t RangeClustering::memory(const RangeClustering::OneDTree<DataContainer, T, Weight, Coreset, Threshold>&)
    size_t memory(const OneDTree<DataContainer, T, Weight, Coreset, Threshold>& tree) {
    ^
    ../RangeClustering/RangeClustering/../RangeClustering/OneDTree.h:157:8: note: template argument deduction/substitution failed:
    ../RangeClustering/RangeClustering/../RangeClustering/OneDTree.h:173:32: note: 'const std::deque<RangeClustering::OneDNode<float, unsigned int, std::vector<std::pair<float, unsigned int>, std::allocator<std::pair<float, unsigned int> > >, double>, std::allocator<RangeClustering::OneDNode<float, unsigned int, std::vector<std::pair<float, unsigned int>, std::allocator<std::pair<float, unsigned int> > >, double> > >' is not derived from 'const RangeClustering::OneDTree<DataContainer, T, Weight, Coreset, Threshold>'
    return memory(nodeContainer) + sizeof(void*) + sizeof(threshold) + sizeof(root);
    ^
    ../RangeClustering/RangeClustering/../RangeClustering/OneDTree.h:162:8: note: template<class T, class Weight, class Coreset, class Threshold> size_t RangeClustering::memory(const RangeClustering::OneDNode<T, Weight, Coreset, Threshold>&)
    size_t memory(const OneDNode<T, Weight, Coreset, Threshold>& node) {
    ^
    ../RangeClustering/RangeClustering/../RangeClustering/OneDTree.h:162:8: note: template argument deduction/substitution failed:
    ../RangeClustering/RangeClustering/../RangeClustering/OneDTree.h:173:32: note: 'const std::deque<RangeClustering::OneDNode<float, unsigned int, std::vector<std::pair<float, unsigned int>, std::allocator<std::pair<float, unsigned int> > >, double>, std::allocator<RangeClustering::OneDNode<float, unsigned int, std::vector<std::pair<float, unsigned int>, std::allocator<std::pair<float, unsigned int> > >, double> > >' is not derived from 'const RangeClustering::OneDNode<T, Weight, Coreset, Threshold>'
    return memory(nodeContainer) + sizeof(void*) + sizeof(threshold) + sizeof(root);
    ^
    ../RangeClustering/RangeClustering/../RangeClustering/OneDTree.h: In member function 'size_t RangeClustering::OneDTree<DataContainer, T, Weight, Coreset, Threshold>::memoryUsage() const [with DataContainer = std::vector<float>; T = float; Weight = unsigned int; Coreset = std::vector<std::pair<float, unsigned int>, std::allocator<std::pair<float, unsigned int> > >; Threshold = double; size_t = unsigned int]':
    ../RangeClustering/RangeClustering/../RangeClustering/OneDTree.h:174:1: warning: control reaches end of non-void function [-Wreturn-type]
    }
    ^


  • Mod

    Ramanujan schrieb:

    Jetzt findet er memory(std::deque<U>) nicht mehr. Die Funktion memory(std::deque<U>) liegt allerdings nicht im Namespace RangeClustering. Ist das das Problem?

    Durch die anderen memory-Deklarationen in RangeClustering wird das deque-memeroy (ich nehme an, das liegt im globalen Namensraum) verdeckt. Und solange U nicht mit dem globalen Namensraum assoziiert ist, kann diese memrory-Überladung folglich nicht (aus RangeClustering::OneDTree<...>::memoryUsage heraus) gefunden werden. Einfache Abhilfe wäre ein qualifizierter Aufruf oder eine entsprechende using-Deklaration in der Funktion oder in RangeClustering.



  • ok, danke, ich habs hinbekommen.


Anmelden zum Antworten