std :: pair <auto、auto>戻り値の型


16

で遊んでいましautostd::pair。以下のコードでfは、関数はstd::pairテンプレートパラメータに依存するタイプのを返すことになっています。

実際の例:

例1

template <unsigned S>
auto f()
{
    if constexpr (S == 1)
        return std::pair{1, 2}; // pair of ints
    else if constexpr (S == 2)
        return std::pair{1.0, 2.0}; // pair of doubles
    else
        return std::pair{0.0f, 0.0f}; // pair of floats
}

これは、gcc 9.2、gcc 10.0、clang 9.0およびclang 10.0で動作します。

次に、明確にするために戻り値の型を明示的に記述したいと思いstd::pairます。

例2

template <unsigned S>
std::pair<auto, auto> f()
{
    if constexpr (S == 1)
        return {1, 2};
    /* ... */
}

gcc 9.2 / 10.0とclang 9.0 / 10.0はどちらもこれをコンパイルできませんでした。

gcc 9.2

error: invalid use of 'auto'
error: template argument 1 is invalid // first argument (auto) of std::pair
error: template argument 2 is invalid // second argument (auto) of std::pair
error: cannot convert '<brace-enclosed initializer list>' to 'int' in return

最後のエラーメッセージから、gcc 9.2はそれstd::pair<auto, auto>がであると信じているようintです。これはどのように説明できますか?

gcc 10.0

error: returning initializer list

このエラーは理解できますが、のコンストラクターstd::pairが呼び出されることを期待していましたか、それともここに欠けているものがありますか?

clang 9.0および10.0

'auto' not allowed in template argument
excess elements in scalar initializer
no matching function for call to 'f'

わかりました、clangはこれを好きではありません。2番目のエラーメッセージから、clangは戻り値の型もであると考えているようですint

最後に、gcc 10.0でコンパイルして取得したエラーを修正するために、std::pair明示的に返すことにしました。

例3

template <unsigned S>
std::pair<auto, auto> f()
{
    if constexpr (S == 1)
        return std::pair{1, 2};
    /* ... */
}

clang 9.0および10.0

以前と同じですが、以下が追加されています。

no viable conversion from returned value of type 'std::pair<int, int>' to function return type 'int'

ここで、clangは、まだint

gcc 9.2

以前と同じです。

gcc 10.0

できます!

いくつかの機能はまだ実装する必要があると思います、または上記の状況の1つで、正しいコンパイラと他の間違ったコンパイラはありますか?私の意見では、例2はうまくいくはずです。それともすべきではないのですか?

回答:


23

構文:

std::pair<auto, auto> f() { return std::pair(1, 2); }
~~~~~~~~~~~~~~~~~~~~~

元のConcepts TSの一部でしたが、C ++ 20の一部であるConceptsプロポーザルには含まれていませんでした。そのため、C ++ 20の唯一のプレースホルダータイプはauto(およびのようなバリエーションauto**decltype(auto)、、および制約付きプレースホルダー(Concept autoおよびそのバリエーション)です。この種のネストされたプレースホルダータイプは非常に便利ですが、C ++ 20の一部ではないため、関数宣言の形式が正しくありません。

今、gccはそれを可能にします。gccがConcepts TSを実装したためです。私は彼らがこの機能を維持することに決めたと思います。clangはTSを実装していないため、実装していません。

いずれにせよ、これ:

std::pair<auto, auto> f() { return {1, 2}; }

常に不正な形式です。構文の意味は、戻り値の型を推定し、pair<T, U>一部の型Tとに一致することを要求することUです。基本的には、発明された関数を呼び出そうとしています。

template <typename T, typename U>
void __f(std::pair<T, U>);

__f({1, 2}); // this must succeed

しかし、型を推測することはできません{1, 2}-ブレース付き初期リストには型がありません。おそらくこれは検討する必要があるものですが(少なくともこのような単純なケースでは理解しやすいため)、許可されたことはありません。したがって、拒否することはどちらの方法でも正しいことです。

最後に:

gcc 9.2はそれstd::pair<auto, auto>がであると信じているようintです。これはどのように説明できますか?

何らかの理由で(おそらく暗黙のCによるCのレガシーが原因int)、gccが型を認識または理解しない場合int、エラーメッセージのプレースホルダーとして使用されます。明らかにintソースコードではなくgccが思いついたので、これは非常に混乱します。しかし、それはそうです。


「braced-init-listに型引数がありません」は私には明確ではありません。std :: pair <int、int> f(){return {1,2}; }は機能し、{1,2}には型がありません(私が理解しているように、std :: pair <int、int>のコンストラクターを呼び出します)。おそらく<auto、auto>を使用すると、コンパイラーは初期化子リスト{1、2}の1と2の型を推定できませんか?
mfnx

@mfnx型引数はありません。型がありません。ブレースされた初期リストは、既知のタイプの初期化など、特定の状況でのみ使用できます。ただし、タイプがないため、控除には使用できません。除いてauto x = {1, 2};動作しますが、すべての種類が同じ場合のみ。
バリー

2
ほとんどのコンパイラーは、最初のエラーで停止するのではなく、最初のエラーから回復して、追加のエラーを報告できるようにします。これは一般に、解析できないものはすべてであると想定することを意味しますintintエラーメッセージのプレースホルダーではありません。コンパイラは本当にそれをであると考えていintます。(これをより明確にするために、gccはおそらくある時点で「仮定のint」と言ったはずです。)
Raymond Chen

2
拡張のもう1つの可能な方法は、動作するため、戻り値の型のクラステンプレート引数の控除を許可することstd::pair __f{1,2};です。
デイビスヘリング

2
@DavisHerring私は本当にstd::optional f() { return 4; }仕事をしたくありません。
バリー
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.