26

次のコードは、C++14 ではおそらく違法ですが、C++17 では合法です。

#include <functional>

int main()
{
    int x = 1729;
    std::function<void (int&)> f(
        [](int& r) { return ++r; });
    f(x);
}

わざわざテストしないでください。一貫性のない結果が得られ、バグなのか意図的な動作なのかを判断するのが難しくなります。ただし、2 つのドラフト (N4140 と N4527、どちらも github.com/cplusplus/draft にあります) を比較すると、[func.wrap.func.inv] に 1 つの大きな違いがあります。パラグラフ 2:

戻り値: R が void の場合は何も、そうでない場合は INVOKE の戻り値 (f, std::forward(args)..., R)。

上記はドラフト間で削除されました。これは、ラムダの戻り値が黙って破棄されることを意味します。これは誤機能のようです。誰も理由を説明できますか?

4

1 に答える 1

21

について規格にとんでもない欠陥がありましたstd::function<void(Args...)>。標準の文言では、(自明ではない) 1の使用std::function<void(Args...)>は合法ではありvoidませんでしvoidた。

void foo() {} std::function<void()> f = foo;C++14 では合法ではありませんでした。おっとっと。

一部のコンパイラは、完全に役に立たない悪い言葉遣いを採用std::function<void(Args...)>し、戻り値が ではない void、渡された callable にのみロジックを適用しました。int次に、std::function<void(Args...)>(またはその他の非void型)に戻る関数を渡すことは違法であると結論付けました。void彼らはそれを論理的な目的に持って行きませんでしたし、同様に戻る関数を禁止しませんでした (std::function要件は、正確に一致する署名の特別なケースを作りません: 同じ論理が適用されます.)

他のコンパイラは、void戻り値の型の不適切な表現を無視していました。

欠陥は基本的に、呼び出し式の戻り値の型がstd::functionの署名の戻り値の型に暗黙的に変換可能でなければならないことでした (詳細については、上記のリンクを参照してください)。また、標準では、暗黙的に2voidに変換することはできません。void

ということで、不具合は解消されました。 std::function<void(Args...)>多くの既存のコンパイラが実装しているように、 で呼び出すことができるものはすべて受け入れArgs...、戻り値を破棄するようになりました。これは、(A) 制限が言語設計者によって意図されていなかった、または (B)std::function戻り値を破棄する方法が望まれていたためだと推測されます。

std::function引数や戻り値の完全な一致は必要なく、互換性だけが必要です着信引数が署名引数から暗黙的に変換でき、戻り値の型が戻り値の型に暗黙的に変換できる場合、それは幸せでした。

そして、タイプの関数は、多くの直感的な定義の下で、「無効なコンテキスト」で実行できるという点でint(int&)署名と互換性があります。void(int&)


1基本的に、operator()コールを正当化するものは許可されませんでした。作成することも、破棄することも、テストすることもできます (そして、それが空であることを知ることもできます)。署名に正確に一致する関数、または関数オブジェクトまたはラムダであっても、関数を指定することはできません。ばかげている。

2void標準の下で暗黙的に変換されるためには、 は void 型の式であるステートメントが有効である必要がありますvoid。type の変数を作成できないため、そのステートメントは無効です。void x = blah;blahvoid

于 2015-10-30T17:22:22.190 に答える