2016-10-16 4 views
2

私が今日取り組もうとしている問題は、参照を返したいところですが、実際には何か「空の」ものを返すため、実際にはできません。「空の」オブジェクトへの参照を返す方法

std::array<std::string, 5> cStrings /* do something to init */ 

const std::string& get_my_string(/* args */) { 
    if(i_know_what_to_return()) { 
     /* 
     * returning happily something in 
     * cStrings if I know what 
     */ 
    } else { 
     /* oeps... i need to return something, I don't have :-(*/ 

     return std::string(); // DON'T TRY THIS AT HOME! 
    } 
} 

オブジェクトのコピーを返す開始し、さらに悪いことに、あらゆる場所に空のオブジェクトの作成を避けるため、またはするための一般的なアプローチが存在しない場合、私は思っていた:ただ、このような何かを明確にします。だから私は私のためにこれを管理するためにコンパイラに伝えることができる何らかの種類のテンプレートクラスを作成できないのだろうかと思っていました。これは、(テンプレート宣言でバラツキの少ないこのすべてがエラーを生成するので、多くのその一つだけが)

template<typename T, decltype(T) V> 
struct empty_reference { 
    using type = T; 
    static const T static_object_; 
    operator T&() const { 
     return static_object_; 
    } 
}; 

template<typename T, decltype(T) V> 
const typename empty_reference<T, V>::type 
    empty_reference<T, V>::static_object_ = V; 

残念ながら、これは動作しません(私は「decltype(T上のエラーを取得する私のアプローチでした)Vは 'decltypeは式が型ではないと期待している'と言っていますが、これは主にテンプレート宣言に何かがないためです。私は、だから私はここに3つの質問を持って

return empty_reference<std::string, std::string()>(); 

を返すことによって、このクラスを使用することを期待しています最後に 。

  1. これは私がこの仕事を作るのですか、私はまだ、コンパイル時に評価されながらVはタイプTである必要があり、コンパイラに伝えるために「decltype(T)V」をどのように有効にする方法
  2. 可能だろうか?
  3. これは良いアプローチか、この問題のより簡単な解決策ですか?
+3

あなたは 'std :: optional'(C++ 17)またはboost.optionalを探しています。 –

+2

"Nullable reference"は "ポインタ"とよく似ていますか? – BoBTFish

+0

@BaummitAugen私は 'optional'を提案しようとしていましたが、参照を含めることはできますか? – BoBTFish

答えて

1

正確には同じではありませんが、戻り値ではなく関数パラメータとして文字列を取得でき、一時参照がconst参照にバインドされているという事実に依存しています。一例として、
:ブーストのようなライブラリがすでにプロジェクトの一部ではなく、あなたがそれらを含めない場合

#include <string> 
#include <iostream> 
#include <utility> 

struct S { 
    template<typename F> 
    void get(bool b, F &&f) { 
     std::forward<F>(f)(b ? s : ""); 
    } 

    std::string s{"foo"}; 
}; 

int main() { 
    S s; 
    s.get(true, [](const auto &str) { std::cout << str << std::endl; }); 
    s.get(false, [](const auto &str) { std::cout << str << std::endl; }); 
} 

は、これは有効な代替することができます。


そうでなければ、他の人が言及しているとして、あなたはstd::optionalと呼ばれる今後の有用性を拾うことができ、それは次のようにstd::reference_wrapperとそれを組み合わせる:

#include <string> 
#include <iostream> 
#include <experimental/optional> 
#include <functional> 

struct S { 
    std::experimental::optional<std::reference_wrapper<std::string>> get(bool b) { 
     return b ? std::ref(s) : std::experimental::optional<std::reference_wrapper<std::string>>{}; 
    } 

    std::string s{"foo"}; 
}; 

int main() { 
    S s; 

    auto opt1 = s.get(true); 
    std::cout << (opt1 ? opt1->get() : "-") << std::endl; 

    auto opt2 = s.get(false); 
    std::cout << (opt2 ? opt2->get() : "-") << std::endl; 
} 

かなり醜い確かに。 std::optionalは、operator boolまたはメンバーメソッドhas_valueを通じて、値が含まれていることを確認する必要があります。

残念ながら、std::reference_wrapperを戻り値として直接使用することはできません。なぜなら、それは(つまり私は)空ではありません)。言い換えれば、そのようなオブジェクトを構築する場合は、そのコンストラクタへの有効な参照を渡す必要があります。


別のアプローチは、次のいずれかのようテンプレートクラスを使用して次のようになります。あなたが構築可能デフォルトされていないこれらのタイプのためにそれを専門にしなければなりません

#include <string> 
#include <type_traits> 
#include <iostream> 

template<typename T> 
struct defval { 
    static const std::decay_t<T> value; 
}; 

template<typename T> 
const std::decay_t<T> defval<T>::value = T{}; 

struct S { 
    const std::string & get(bool b) { 
     return b ? str : defval<std::string>::value; 
    } 

    std::string str{"foo"}; 
}; 

int main() { 
    S s; 
    std::cout << s.get(true) << std::endl; 
    std::cout << s.get(false) << std::endl; 
} 

注意。

+0

まあまあ、テンプレート 'class empty_ref'を作成する考え方は有効なオブジェクトを作成するようにコンパイラに指示して、参照を返すために使用できるものを指定します。文字列オブジェクトは、文字列だけでなく、ユーザー定義型に対しても実行するため、問題を説明するための例に過ぎませんでした。 – user2546926

+0

@ user2546926関数を受け入れる逆引きのアプローチは、あらゆる型の問題を解決します。 – skypjack

+0

@ user2546926必要に応じて使用されるデフォルト値を導入するテンプレートクラスに基づく別のアプローチを追加しました。 – skypjack

0

参照はオブジェクトにバインドされている必要があります。あなたのケースでは参照を返すことはできません。あなたのオプションは:

  1. (所有していない)ポインタを返します。
  2. 参照を保持できるboost::optionalのようなものを返します(間もなく追加されるstd::optionalとは異なります)。
+0

'std :: optional'は参照を保持できません。[here](http://eel.is/c++draft/optional#syn-1)(作業草案) - _テンプレートのインスタンス化を必要とするプログラム参照型[...]はill-formed_です。 – skypjack

+0

@skypjack、それは残念です。 – StoryTeller

+0

それは可能かもしれませんが、私のせいにしてはいけません! :-) – skypjack

-1

私はそれを考え出したので、少なくとも私は推測します。

template<typename T> 
struct empty_reference { 
    using type = T; 
    static const type static_object_; 
    operator const type&() { 
     return static_object_; 
    } 
}; 

template<typename T> 
const typename empty_reference<T>::type 
    empty_reference<T>::static_object_ = {}; 

コンセプトは、コンパイラが使用するすべてのタイプのstatic 'static_object_'メンバーを1つ '生成'することです。したがって、empty_referenceを使用したすべてのタイプでは、アプリケーションで静的オブジェクトが生成され、要求されたときに返されます(つまり、異なる2つのファイルにempty_reference<int>を返すと、同じint値への参照が返されます)。

decltype(T) V」を完全に削除し、ユニバーサル初期化(すなわち、{})を使用して、オブジェクトを初期化するために「空の」コンストラクタが呼び出されたことを確認しました。

欠点は一定である必要があります(それ以外の場合は、空の参照を編集できます)。型は空のコンストラクタを使用して構築可能である必要があります。

私は残念なことに、それは良いアイデア(私は自分のアイデアに行くつもりはない)かどうかということです。他の提案/フィードバックをお寄せください:

関連する問題