あいまいなオーバーロードテンプレート


16

次のテンプレートコードがあります

#include <vector>
#include <array>
#include <iostream>

template<typename T1>
void foo(std::vector<T1> bar) {
    std::cout << "GENERIC" << std::endl;
}

template<typename T1>
void foo(std::vector<std::vector<T1>> bar) {
    std::cout << "SPECIFIC (vector)" << std::endl;
}

template<typename T1, int SIZE>
void foo(std::vector<std::array<T1, SIZE>> bar) {
    std::cout << "SPECIFIC (array)" << std::endl;
}

int main() {
    std::vector<std::vector<int>> a(2, std::vector<int> { 1, 2, 3});
    std::vector<std::array<int, 3>> b(2, std::array<int, 3> {4, 5, 6});

    foo(a);
    foo(b);
}

生成する

SPECIFIC (vector)
GENERIC

vector-of-vectorバージョンが特定のテンプレートで呼び出されるのはなぜですか?しかし、vector-of-arrayバージョンはジェネリックで呼び出されるのですか?


2
参考:vectorすべてのアウターを削除することで、同じ問題でこれを簡略化できます。ここを参照
ChrisMM

@ChrisMM良いキャッチ。この例は、ネストされた構造が必要な私のプロダクションコードから合成されました。
Xaser

5
MSVCはベクターの配列バージョンを呼び出します:godbolt.org/z/7Gfeb0
R2RT

回答:


8
template<typename T1, size_t SIZE>
void foo(std::vector<std::array<T1, SIZE>> bar) {
    std::cout << "SPECIFIC (array)" << std::endl;
}

std::size_t代わりに使用する必要がありますintここを走る

編集: 実際、あなたのコメントとコードに関する私の直感が私をトピックに掘り下げました。一見したところ、(私のような)標準的な開発者は、コンパイラintstd::size_t(両方とも整数型であり、暗黙的に変換するのは非常に簡単であるため)に変換しvoid foo(std::vector<std::array<T1, SIZE>> bar)、最適な特殊化として選択することを期待しています。だからテンプレート引数の控除ページを読んでいるときに私はこれを見つけました:

非タイプテンプレートパラメータがパラメータリストで使用され、対応するテンプレート引数が推定される場合、推定テンプレート引数のタイプ(そのテンプレートテンプレートリストで指定されているとおり、参照が保持されることを意味します)は、 cv-qualifiersが削除されていること、およびテンプレートの引数が配列の境界から推定される場合を除いて、正確に非型テンプレートパラメーター。この場合、整数型は許可され、ブール値であっても常にtrueになります。

もちろん、いつものように、それが何を意味するかを理解するために、一度よりも数回読む必要があります:)

だから面白い結果が出てきます。

必要な特殊化はすでに選択されていませんが、コンパイラーが強制的に選択した場合はエラーになります。

template<typename T1, int SIZE>
void foo(std::vector<std::array<T1, SIZE>> bar) {
    std::cout << "SPECIFIC (array)" << std::endl;
}

int main() {
    std::vector<std::array<int, 3>> b(2, std::array<int, 3> {4, 5, 6});

    foo(b); // P = std::vector<std::array<int,(int)SIZE>
            // A = std::vector<std::array<int,(unsigned_long)SIZE>>
            // error: deduced non-type template argument does not have the same
            // type as its corresponding template argument */
}

コードを実行する

別の興味深いことは次のとおりです。

非型テンプレート引数が推定されなかった場合、引数とテンプレート型を強制的に同じにする制限はありません。

#include <vector>
#include <array>
#include <iostream>

template<typename T1, int SIZE>
void foo(std::vector<std::array<T1, SIZE>> bar) {
    std::cout << "SPECIFIC (array)" << std::endl;
}

int main() {
    std::vector<std::array<int, 3>> b(2, std::array<int, 3> {4, 5, 6});

    foo<int,3>(b);
}

コードを実行する


@Xaserは、配列の2番目のテンプレート引数の型がsize_t...であるため
Jean-BaptisteYunès

2
R2RTのコメントを考慮すると、コンパイラ固有の違いがあるようです。
Xaser

8

私が考えて、これは単にから1行によるものです[temp.deduct.call]/4

一般に、推定プロセスは、推定されたAをAと同一にするテンプレート引数値を見つけようとします。

明確にAするために、パラメータを意味します[temp.deduct.call]/1

...呼び出しの対応する引数のタイプを使用したテンプレート引数の推定(Aと呼びます)...

すでに指摘したように、変更template<typename T1, int SIZE>することで、発生してtemplate<typename T1, size_t SIZE>いる問題が修正されます。で述べたよう[temp.deduct.call]/4に、コンパイラはAと同じを推定しようとしていAます。にstd::arrayはテンプレート引数<class T, size_t N>(から[array.syn])があるため、2番目のパラメーターは実際size_tにはであり、ではありませんint

そのため、テンプレート控除のために、あなたの一般的な機能は、template<typename T1>一致することができ、正確の種類A、-としてご専門でtemplate<typename T1, int SIZE>はない正確に一致します。MSVCの控除は正しくないと思います。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.