2

次のプログラムは、2 つの別個の名前空間 (および) で 2つの関数テンプレートA::foo<>()および を定義します。2 つの関数テンプレートは署名が同じで、2 番目のテンプレート パラメーターに割り当てられた既定の引数のみが異なります。最終的に、それらの名前は、対応する宣言のペアによってのスコープに取り込まれます。B::foo<>()ABmain()using

#include <type_traits>

namespace A
{
    template<
        typename T,
        typename = typename std::enable_if<
            std::is_same<T, int>::value        // Let this be condition C
            >::type
        >
    void foo(T) { }
}

namespace B
{
    template<
        typename T,
        typename = typename std::enable_if<
            !std::is_same<T, int>::value       // This is the negation of C
            >::type
        >
    void foo(T) { }
}

int main() {
    using A::foo;
    using B::foo; // COMPILES: Is this legal?

    foo(42); // Invokes A::foo(), non-ambiguous because of SFINAE
}

ここでは、2 番目の宣言によってコンパイル エラーが発生すると予想usingされます。結局のところ、同じ名前空間でこれら 2 つのテンプレートを定義しようとすると、エラーが発生します。

驚いたことに、これを試したすべてのコンパイラ (GCC 4.7.2、GCC 4.8.0 ベータ、ICC 13.0.1、Clang 3.2) は、プログラムをコンパイルし、A::foo().

質問 #1: これは正しいですか? おそらく「診断不要」のケースでしょうか?C++11 標準への参照が推奨されます。


上記のプログラムのこのバリエーションを考えてみましょう。これは基本的に、名前空間ではなくクラスを使用して同じ効果を実現します。

#include <type_traits>

struct X
{
    template<
        typename T,
        typename = typename std::enable_if<
            std::is_same<T, int>::value        // Here is condition C again
            >::type
        >
    static void foo(T) { }
};

struct Y
{
    template<
        typename T,
        typename = typename std::enable_if<
            !std::is_same<T, int>::value       // And the negation of C again
            >::type
        >
    static void foo(T) { }
};

struct Z : X, Y
{
   using X::foo;
   using Y::foo; // COMPILES: Is this legal?
};

int main() {
    Z::foo(42); // Invokes X::foo(), non-ambiguous because of SFINAE
}

このプログラムも上記のすべてのコンパイラでコンパイルされますが、2 番目のusing宣言によってコンパイラ エラーが発生すると予想されます。

質問 #2: これは正しいですか? おそらく「診断不要」のケースでしょうか?C++11 標準への参照が推奨されます。

4

1 に答える 1

4

質問1の場合、これを禁止するルールがないため、これは許可されているようです。C ++ 11標準では、using宣言によって導入された関数が、using宣言によって名前が導入された名前空間で直接宣言された関数と競合する場合、これを禁止するルールの横にある注記でこれに言及しています。

§7.3.3[namespace.udecl]/14に記載されています:

名前空間スコープまたはブロックスコープの関数宣言が、using-declarationによって導入された関数と同じ名前および同じパラメーター型を持ち、宣言が同じ関数を宣言しない場合、プログラムは不正な形式になります。[...]

これは、ある種の競合を無効として指定する規範的なテキストです。

2つのusing宣言が同じように競合しないという規範的なテキストはありませんが、2つのusingdeclaractions間の同様の競合は宣言の時点で無効ではないことに注意してください。同じ段落が続きます:

[...] [注:2つのusing宣言により、同じ名前と同じパラメーター型の関数が導入される場合があります。修飾されていない関数名の呼び出しで、関数のオーバーロード解決がそのようなusing-declarationsによって導入された関数を選択した場合、関数呼び出しの形式は正しくありません。[ 例:

 namespace B { 
   void f(int); 
   void f(double); 
 }
 namespace C { 
   void f(int);
   void f(double); 
   void f(char);
 } 

 void h() {
   using B::f;    // B::f(int) and B::f(double)
   using C::f;    // C::f(int), C::f(double), and C::f(char)
   f(’h’);        // calls C::f(char)
   f(1);          // error: ambiguous: B::f(int) or C::f(int)?
   void f(int);   // f(int) conflicts with C::f(int) and B::f(int)
 }

—終了例] —終了ノート]

質問#2については、クラスメンバー宣言とクラスレベルの使用宣言との間の競合の場合を説明する類似の規範的テキストは、次の段落、§7.3.3/15にあります。

using-declarationが基本クラスから派生クラススコープに名前をもたらす場合、派生クラスのメンバー関数とメンバー関数テンプレートは、同じ名前のメンバー関数とメンバー関数テンプレートをオーバーライドおよび/または非表示にします。parameter-type-list(8.3 .5)、(競合するのではなく)基本クラスのcv-qualification、およびref-qualifier(存在する場合)。[注:コンストラクターを指定するusing-declarationsについては、12.9を参照してください。—エンドノート]

ここでも、宣言の使用間の競合に関するテキストはありませんが、前の場合の注記と同様に、競合する関数を指定する可能性のある2つの使用宣言があることは、これらの宣言の共存を禁止するテキストがないため、不正な形式ではありません。そして、前の場合のように:関数呼び出しの場合、過負荷解決がこれらの関数の両方を選択した場合、呼び出しは不正な形式になります。

あなたの例では、SFINAEは常に、潜在的に競合する関数の1つをオーバーロードセットから削除するため、どちらの場合も問題はありません。

于 2013-03-01T00:48:48.533 に答える