2016-03-24 5 views
3

私が現在取り組んでいるコードについては、私が見ているエラーメッセージで困惑しています。私は、目で見やすくするために最も関連性の高いコードを抽出しようとしました。テンプレート上の致命的なエラー

エラーは私が参照してください。

error: no matching function for call to ‘ Map(Print&, std::shared_ptr< LinkedList< int> >&)
note: template argument deduction/substitution failed: cannot convert ‘ p ’ (type ‘ Print ’) to type ‘ int

なぜコンパイラは全くPrintintにをキャストしようとしていますか?読書のための

template<typename T> 
class LinkedList 
{ 
public: 
    using NodeType = std::shared_ptr<LinkedList>; 
    ... Other things 
} 

template<class Func, typename T> 
typename LinkedList<T>::NodeType Map(Func func, typename LinkedList<T>::NodeType seq) 
{ 
    // Some stuff 
} 

class Print 
{ 
public: 

int operator()(int i) 
{ 
    cout << i << endl; 
    return i; 
} 
}; 

void main() 
{ 
    Print p; 
    auto one = std::make_shared< LinkedList <int>>(); 
    auto result = Map<int>(p, one); << ---- Error 
} 

のgcc 4.8.4

感謝。

+3

あなたは、コンパイラに 'Func = int'ここで' Map (p、one) 'と言っています。 ' – Praetorian

+0

' auto result = Map (p、one); 'これは' print'を 'int'に参照しようとしていませんか? – AchmadJP

+0

テンプレートパラメータを入れ替えた場合、 'Map (p、one)'を指定し、 'Func'を' Print'として推測させることができます。 – melak47

答えて

1

私がこれをやっていたら、MapをLinkedListのインラインフレンドとして定義します。これにより従属型の控除に関する問題がなくなり、明示的なテンプレートパラメータなしでMapを呼び出すことができます。

コンパイラがこのように定義された関数を見つける唯一の方法は、引数依存の参照であることに注意してください。LinkedListオブジェクトが関数の引数の1つとして渡された場合のみ動作します。また、インラインの友人は人々を変質させるようです。

しかし、それはコードをクリーンアップして単純化するための長い道のりです。

#include <iostream> 
#include <memory> 

template<typename T> 
class LinkedList 
{ 
public: 
    using NodeType = std::shared_ptr<LinkedList>; 


    template <typename Func> 
    friend NodeType Map(Func func, NodeType seq) { 
     return seq; 
    } 

}; 

class Print 
{ 
public: 
    int operator()(int i) 
    { 
     std::cout << i << std::endl; 
     return i; 
    } 
}; 

int main() 
{ 
    Print p; 
    auto one = std::make_shared< LinkedList<int> >(); 
    auto result = Map(p, one); 
} 
+0

これは私が最初に実装したものです。私はClojure/LispでMap関数をエミュレートしようとしています。 MapをLinkedListのメンバーにすることは、それがプライベートメンバーにアクセスできることを意味し、MapはLinkedListのプロパティではありません。それはLinkedListの操作でなければなりません。後でこれをより一般的な形式に拡張したいと思います。任意の順序でマップします。 – Cyrax

+0

Well Mapはメンバーではなく、単なる友達です。メンバーは 'p-> Map(one)'のように使われるのに対し、 "Free"関数 'Map(p、one)'として使われます。しかし、それはプライベートメンバーへのアクセス権を持っており、最終的な実装が大きければ、クラス定義を実際に醜くする可能性があります。 –

0

OPの目標をよりよく理解した上で、新しい回答を追加します。私の最初の答えは、いくつかのサンプルコードをコンパイルするときに遭遇する依存テンプレートの型パラメータに関連する問題を扱ったので、それはまだ有効な答えだと思います。

この回答は、カスタムコンテナクラスを作成する方法と、コンテナを反復処理し、各要素に対してカスタム関数オブジェクトを呼び出し、各結果を関数によって最後に返された別のコンテナに追加する汎用関数の記述方法を示しています。

この例のコンテナは、最低限のフィーチャセットで定義されたリンクリストです。この例では、コンパイルおよび実行に十分です。

リンクリストクラスは、リストをトラバースするために使用されるイテレータオブジェクトを返すbegin()およびend()関数を提供します。ここでは定数でないバージョンだけが定義されていますが、LinkedListオブジェクトがconstの場合には定数の反復子を返すバージョンも追加する必要があります。

ここでは、関数Mapはファンクタとコンテナオブジェクトを引数として取ります。すべての引数型が導出されるので、テンプレートパラメータを明示的に指定する必要はありません。コンテナはLinkedListのconst正当性の問題を避けるために値渡しされます。また、LinkedList(デフォルト)のコピーコンストラクタは多くの作業を行わないため、オブジェクトを2つだけコピーする必要はありません。含まれているすべてのデータの完全コピー。

Mapこれは非常に単純で、追加の改良されたバージョンは2つのイテレータ(開始と終了)を取り、コンテナオブジェクトへのconst参照を(無駄なコピーを避けるため)取るか、または型の特性を使用して特別な方法でアクセスできます。

Mapは、標準イテレータAPIを使用しているため、std :: vectorなどの他のコンテナで動作する必要があります。

元のバージョンのLinkedListは、ただ1つのリストノードへのポインタとして維持されていました。以下のコードでは、LinkedListは、最初と最後のNodeオブジェクトへの(スマートな)ポインタを保持する実際のコンテナオブジェクトです。ここでは、headtailの両方のノードへのポインタにアクセスする必要があるため、1つのノードへのポインタを保持することは不十分です。

#include <iostream> 
#include <memory> 
#include <string> 

template<typename T> 
class LinkedList { 
public: 
    struct Node; 
    using NodeType = std::shared_ptr<Node>; 
    struct Node { 
     NodeType next; 
     T   value; 
    }; 

    class forward_iterator { 
     NodeType cur_node; 
    public: 
     forward_iterator() {} 
     forward_iterator(NodeType cur_node) : cur_node(cur_node) {} 
     T& operator *() { return cur_node->value; } 
     forward_iterator& operator ++() { cur_node = cur_node->next; return *this; } 
     bool operator == (const forward_iterator& it) const { return cur_node == it.cur_node; } 
     bool operator != (const forward_iterator& it) const { return !operator == (it); } 
    }; 

    void push_back(const T& t) { 
     NodeType node = std::make_shared<Node>(); 
     if(tail) { 
      tail->next = node; 
      tail = node; 
     } else { 
      head = tail = node; 
     } 
     node->value = t; 
    } 

    forward_iterator begin() { return forward_iterator(head); } 
    forward_iterator end() { return forward_iterator(); } 

protected: 
    NodeType head, tail; 
}; 

inline std::string upper_case(const std::string& s) { 
    std::string r; 
    for(auto c : s) { 
     if(c >= 'a' && c <= 'z') c = c - 'a' + 'A'; 
     r.push_back(c); 
    } 
    return r; 
} 

template <typename Func, typename S> 
inline S Map(Func func, S seq) { 
    S result; 
    for(const auto& elem : seq) { 
     result.push_back(func(elem)); 
    } 
    return result; 
} 


int main() { 
    // add strings to a LinkedList of strings named "original" 
    static const char* my_data[] = { "Hello", "1234", "John Cena", "xd" }; 
    LinkedList<std::string> original; 
    for(auto cstr : my_data) { original.push_back(cstr); } 

    // dump "original" to cout 
    std::cout << "-- original --\n"; 
    for(const auto& s : original) { std::cout << s << '\n'; } 
    std::cout << '\n'; 

    // Run the generic Map function with "original" as input 
    // A lambda will be called for each element and 
    // its returned value added to the result, named "mapped". 
    // A functor object may be used in place of the lambda 
    auto mapped = Map(
     [](const std::string& s) { return upper_case(s); }, 
     original); 

    // dump "mapped" to cout 
    std::cout << "-- mapped --\n"; 
    for(const auto& s : mapped) { std::cout << s << '\n'; } 
    std::cout << '\n'; 
}