2017-11-18 2 views
-2

私はこれを最小限の検証可能な例にする方法に苦労していますが、どうやってそれを行うのか考えることはできません。私は一緒に追加しようとしている2つの予想されるタイプがあります。予想されるデータ型は、int、doubleなどのデータ型にすることができます。例外でもかまいません。理論的には、例外を持つデータ型を追加することができなければなりません。値を求めても、プログラムを正常に実行することができます。プログラムをクラッシュさせずに例外を返す必要があります。テンプレート型のC++オペレータオーバーロードでエラーが発生する

このプログラムを実行しようとするたびに、何処から始めても何もわからないエラーメッセージが数百行あります。私が見ているエラーはno matching function for call to 'operator+(double&,double&)です。これが正しいかどうかわかりません。 2つのExpectedを一緒に追加するには、型自体を追加したくありません。最後に、Expectedを追加して返され、Expectedを取得したいと思います。

私は本当にここにこだわっていますが、私はapplyの機能が正しく実装されていないと言われましたが、正にその理由がわかりません。私は間違って何をしていますか?

#include <stdexcept> 
#include <exception> 
#include <functional> 
#include <variant> 

template<typename T> 
class Expected 
{ 

public: 
    Expected(T t) : state(t), valid(true){} 
    Expected(std::exception_ptr e) : state(e), valid(false){} 
    Expected(std::exception e) : state(std::make_exception_ptr(e)), valid(false){} 

    T value() const 
    { 
     if(valid) return std::get<T>(state); 
     std::rethrow_exception(std::get<std::exception_ptr>(state)); 
    } 

    bool isValid() 
    { 
     if(valid) return true; 
     return false; 
    } 

    template<typename U> 
    Expected<U> apply(std::function<U(T)> f) 
    { 
     if(!valid) return std::get<std::exception_ptr>(state); 
     try 
     { 
      return f(std::get<T>(state)); 
     } 
     catch(...) 
     { 
      return std::current_exception(); 
     } 
    } 
private: 

    std::variant<T, std::exception_ptr> state; 
    bool valid; 

}; 

template<typename T> 
std::ostream& operator<< (std::ostream& o, Expected<T> e) 
{ 
    try 
    { 
     o << e.value(); 
    } 
    catch(std::exception &a) 
    { 
     o << a.what(); 
    } 
    catch(...) 
    { 
     o << "Unexpected Error"; 
    } 

    return o; 
} 

template<typename T, typename V> 
auto operator+(Expected<T> t, Expected<V> v) 
{ 
    return t.apply([&](T myT){return operator+(myT,v);}); 
} 
template<typename T, typename V> 
auto operator+(Expected<T> t, V v) 
{ 
    return t.apply([&](T myT){return operator+(myT,v);}); 
} 
template<typename T, typename V> 
auto operator+(V v, Expected<T> t) 
{ 
    return t.apply([&](T myT){return operator+(v,myT);}); 
} 

int main() 
{ 
    Expected<int> a = 1; 
    Expected<int> b = 2; 

    std::cout << a + b << std::endl; 
} 
+0

ここで 'valid'は厳密には必要ありません。 'variant :: which'を呼び出すと、格納されている値の型のインデックスを取得できます。 –

+0

@ MaximEgorushkinは有効ですか? 'isValid()'またはブール値 'valid'を一緒に使う? – Sailanarmo

+0

'valid.'データメンバーは' state.which()== 0'として計算できるので不要です。 –

答えて

4

は、最初の問題の減少した例です:

int main() { 
    operator+(1, 2); // error 
} 

あなたが名前で組み込み演算子を呼び出すことはできません。オペレーターによってのみ呼び出すことができます。さらに、演算子を使用するだけで、とにかく読みやすくなります。そのようにしてください。 +を使用してください。 (コードの一部で、おそらくoperator+の代わりにopを使用しています)。ここで


は、第二の問題の減少した例です:

template <typename T> 
struct X { 
    template<typename U> 
    U apply(std::function<U(T)> f); 
}; 

int main() { 
    X<int>{}.apply([](int){return 2.0;}); // error 
} 

は基本的に、std::functionを推測することはほとんど常に間違っています。このラムダであり、std::functionではなく、であり、std::function<double(int)>ではありません。あなたがしたいことは、ほとんどの場合、任意の呼び出し可能なものを推論し、メタ関数を使用して結果を判定します。

template <typename T> 
struct X { 
    template <typename F, typename U = std::invoke_result_t<F&, T>> 
    U apply(F f); 
}; 
+1

「期待」は「U」ではありません。 – Yakk

+0

さて、申し訳ありませんが、これは私の能力の範囲を超えて問題を取り上げるのは初めてのことなので、私は本当にこのすべてに悩まされました。あなたの答えをありがとう。ラムダが 'std :: function'ではないと言ったときに説明することができますか?私はその機能の結果をキャプチャしようとしていると思った。 – Sailanarmo

+0

@Sailanarmo私はただそれを意味します。ラムダは 'std :: function'ではありません。正しいパラメータ型の場合、ラムダから 'std :: function'を構築することができますが、それは同じことではありません。同じように 'const char *'は 'std :: basic_string 'ではありませんが、 'const char *'から 'std :: basic_string 'を作ることができます。 – Barry

3

まずオフ:一般的に、あなたはあまりにも多くのエラーを取得している場合、あなたは、コード内の1つの問題に至るまでですまで(つまり、まだ複数として報告することができ、いくつかのコードを除外して、テストケースを簡素化エラー、しかし)。そのエラーを理解するか、具体的なことを聞​​かせてください。この場合、これはoperator +という1つのオーバーロードで開始することを意味し、operator <<はありません。


ここには複数の問題があります。先ず、なぜ論理a + bの代わりにoperator+ (a, b)と呼んでいますか?これは、実際には、operator+関数が2つのdoubleを取っていないため、質問で明示的に言及したエラーが発生します。組み込み演算子+は関数ではなく、そのように呼び出すことはできません。

第2の問題は、ラムダ式の型がstd::functionではないため、テンプレートパラメータUapplyであることをラムダ式から推論できないことです。これは、あなたがそれのための明示的なテンプレート引数を提供しなければならないことを意味:

return t.template apply<decltype(std::declval<T>() + v)>([&](T myT){return myT + v;}); 

この変更はoperator +のすべての3つのオーバーロードで発生する必要があります(私は第三過負荷に未知のopを想定することは奇妙なタイプミスで、実際にもする必要があります+と呼んでください)。

キーワードがapplyより前に必要であることに注意してください。依存コンテキストで使用されているためです。

Barry's answerは、std::functionを完全に取り除くことにより、より良いアプローチを提供することに注意してください。

最後に、#include <iostream>が見つかりません。

これらの変更がすべて行われた場合、コード[works]。ここで

+0

'apply'は最初にstd関数を使うべきではありません。型消去クラスの型を推測すると、コードの匂いであり、ここでコードが破られます – Yakk

+0

@Yakk True true。ただし、戻り値の型に簡単にアクセスできます。私はその間に投稿された回答を見る。 – Angew

+0

さて、これは間違いなくすべてのエラーを修正しました。そして、あなたの '演算子+'の説明は間違いなく私を助けてくれました。 'apply'が' std :: function'を取るべきではないのはなぜですか?私はあなたのコードを使用する場合、それはそれがちょうど良い方法のように思える。 – Sailanarmo

関連する問題