14

最小限のプログラム:

#include <stdio.h>

#include <type_traits>

template<typename S, typename T>
int foo(typename T::type s) {
    return 1;
}

template<typename S, typename T>
int foo(S s) {
    return 2;
}

int main(int argc, char* argv[]) {
    int x = 3;
    printf("%d\n", foo<int, std::enable_if<true, int>>(x));

    return 0;
}

出力:

    1 

これでコンパイルエラーにならないのはなぜですか? テンプレート コードが生成されると、関数int foo(typename T::type search)int foo(S& search)は同じ署名を持ちませんか?

テンプレート関数のシグネチャを少し変更しても、まだ機能します (上記の例で期待したとおりです)。

template<typename S, typename T>
void foo(typename T::type s) {
    printf("a\n");
}

template<typename S, typename T>
void foo(S s) {
    printf("b\n");
}

ただし、これはそうではなく、唯一の違いは、一方が int シグネチャを持ち、もう一方が最初のテンプレート パラメーターによって定義されていることです。

template<typename S, typename T>
void foo(typename T::type s) {
    printf("a\n");
}

template<typename S, typename T>
void foo(int s) {
    printf("b\n");
}

コンパイラ エラー (Clang):

test.cpp:26:2: error: call to 'foo' is ambiguous
foo<std::enable_if<true, int>>(3);
^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
test.cpp:16:6: note: candidate function [with T = std::__1::enable_if<true, int>]
void foo(typename T::type s) {
        ^
test.cpp:21:6: note: candidate function [with T = std::__1::enable_if<true, int>]
void foo(int s) {
        ^
1 error generated.

私が取り組んでいるプロジェクトでこれに似たコードを使用していますが、特定のケースで未定義の動作を引き起こす、私が理解していない言語に微妙にあるのではないかと心配しています。また、Clang と VS11 の両方でコンパイルされることにも言及する必要があるため、単なるコンパイラのバグではないと思います。


編集: 2 番目のケース (タイプミス) を修正しました。Clang からのエラー メッセージを追加しました。

編集#2: T::type の意味を尋ねた人のために。

http://en.cppreference.com/w/cpp/types/enable_ifから:

template< bool B, class T = void > struct enable_if;

B が true の場合、std::enable_if は T と等しい public メンバ typedef 型を持ちます。それ以外の場合、メンバ typedef はありません。

enable_if は構造体です。基本的に、enable_if の最初のテンプレート パラメーターで評価された式が true の場合 (上記の例の場合は true)、type2 番目のテンプレート パラメーターと同じ型を持つパブリック メンバーが存在します。

の場合enable_if<true, int>、enable_if::type の型は int です。

4

2 に答える 2

8

最初の関数は、最初の関数よりも特殊化されていると見なされます。

関数

int foo(typename T::type)

一致する可能性があります

template <typename S,typename T> int foo(S s)

パラメータ S の値として T::type を使用しますが、

int foo(S s)

一致しません

template <typename S,typename T> int foo(typename T::type)

T は推定できないからです。

ロジックは、C++03 標準のセクション 14.5.5.2 と、C++11 標準のセクション 14.5.6.2 に配置されています。

ある関数が別の関数よりも特殊化されているかどうかを確認するには、最初の関数の各テンプレート パラメーターの値を作成し、2 番目の関数が結果のシグネチャと一致するかどうかを確認します。また、2 番目の関数のテンプレート パラメーターの値を発明し、最初の関数が結果のシグネチャと一致するかどうかを確認します。2 番目の関数が最初の関数と一致する場合、2 番目の関数は最初の関数よりも特殊化できません。それに加えて、最初の関数が 2 番目の関数と一致しない場合、最初の関数は 2 番目の関数よりも特殊化されている必要があります。それはあなたが持っている場合です。

于 2012-12-20T06:17:40.363 に答える
5

この現象をさらに単純化すると、次のようになります。

#include <stdio.h>

template<typename T>
void foo(int arg) {
    printf("a\n");
}

template<typename T>
void foo(T arg) {
    printf("b\n");
}

int main(int argc, char* argv[]) {
    foo<int>(3);   // prints "a"
    foo(3);        // prints "b"

    return 0;
}

テンプレート パラメーターは、山かっこを使用して明示的に渡すか、演繹によって解決できます。パラメーターが明示的に指定されていない場合は、関数の引数を使用して推定できる必要があります。

したがって、 の場合foo(3)、パラメータ T が明示的に指定されておらず、推定できないため、テンプレート 'a' は機能しません。の場合foo<int>(3)、両方のテンプレートが機能します。実際、テンプレート 'a' をコメントアウトすると、 を呼び出すとfoo<int>(3)"b" が出力されます。問題は、なぜテンプレート 'a' が好まれるのかということです。ここで重要なのは「部分的な順序付け」です。

http://publib.boulder.ibm.com/infocenter/lnxpcomp/v8v101/index.jsp?topic=%2Fcom.ibm.xlcpp8l.doc%2Flanguage%2Fref%2Fpartial_ordering_funct_templ.htm

他の誰かがすでに回答していることがわかりました (私は質問にすぐに答えるのが苦手です) ので、これをまとめて、テンプレート 'a' は Vaughn が言ったようにより特殊化されていると言います。

于 2012-12-20T06:41:51.760 に答える