9

C++ には ADL (Argument Dependent Lookup) があり、その名前が示すように、関数のコンテキスト (名前空間) を引数 (のいずれか) のコンテキスト (名前空間) から暗示することができます。

fun(a); // if the type of a is in namespace ns deduce ns::f if available

私の質問は、何らかの手法で逆も可能かどうかです。逆に言えば、呼び出された関数のコンテキストからコンテキスト (名前空間) を推測できるかどうかを意味します。ある種の「関数依存ルックアップ」(FDL)。偽のコード:

ns::fun(a); // deduce ns::a if available

私はそれを行う方法を理解できません。この制限は、enum関数のオプションをエンコードするために使用される s にとって特に厄介です。この機能をシミュレートする手法があるかどうか知りたいです (C++11 も問題ありません)。偽のコード:

ns::fun(Saturday, Tuesday); // Saturday/Tuesday are enum values in namespace ns;

特にenums の回避策がある場合。

このコードは問題を示しています。

namespace longname{
    class A{};
    void fun(A const& a){}
    A global_a;

    enum Days { Saturday,Sunday,Tuesday,Wednesday,Thursday,Friday};
    void gun(Days d1, Days d2){}    
}

int main(){
    longname::A a;
    fun(a); // cool, longname::fun(a) not necessary, fun is deduced from context

    longname::fun(global_a); // error, not cool, global_a context not deduced, 
    // must use then longname::fun(longname::global_a)
    longname::gun(Saturday, Tuesday); // error, particularly not cool, the Saturday is not deduced from context 
    // must use then longname::gun(longname::Saturday, longname::Tuesday)
    // or at best gun(longname::Saturday, longname::Tuesday)
}

編集: @jrok は、ネストされた名前空間の定義に基づく回避策を提案しました。このenum場合、このコードを取得します。これにはまだ多少のノイズがあります (実際には「依存」ルックアップはまったくありません) が、改善されています。

namespace longname{
    namespace days{
        enum _ { Saturday,Sunday,Tuesday,Wednesday,Thursday,Friday};
    }
    void gun(days::_ d1, days::_ d2){}  
}

int main(){
    using namespace longname::days; // some noise still here
    longname::gun(Saturday, Tuesday);
}

、 などをスコープ内で直接使用できないenum classため、Saturday使用していません(実際にはコンパイルエラーが発生します)Sundayusing longname::days::_

4

1 に答える 1

3

はいといいえ。ほとんどの場合、いいえ。

悪いニュースは、enum が などの現在のスコープ外にあるTuesday場合、その関数が enum が表示される名前空間で宣言されていたとしても、それを関数に渡すことができないことです。これは、関数呼び出しを記述するときに最初に引数ルックアップが発生し、引数を に渡してからgun名前ルックアップを発生させることができないためです。これを変えることはできませんが、良いニュースもあります。

まず、マップする動作が必要なようですns::foo(arg1, arg2)-> {using namespace ns; ns::foo(arg1, arg2);}。関数呼び出しとテンプレートはこれを変更できませんが、マクロは変更できます。例を含めました。

また、引数依存ルックアップの基本的な例を示しました。範囲外の関数 GetMonday と GetTuesday (範囲外の列挙型を返す) は、名前空間から 1 つの型を含めたという理由だけで、このメカニズムを使用して見つけることができます。RegisterNamespace::valは、コンパイラが GetMonday を見つけようとするときに隠し名前空間をスコープに追加し、GetMonday はDays、コンパイラが を見つけられるようにする a を返しますfoo

本当に、別の名前空間からの関数に遭遇したときに、追加の名前空間を追加することによって、コンパイラがスコープを変更することを望んでいます。ただし、コンパイラはそれまでに引数の型をすでに決定しており、実際には関数の他の可能な代替案を解決する必要があります。

#include <iostream>

namespace hidden {

enum RegisterNamespace { val };

enum Days {
    Monday,
    Tuesday
};

void foo(Days a , Days b){std::cout << "Called foo\n";}

Days GetMonday(RegisterNamespace a = val){return Days::Monday;}
Days GetTuesday(RegisterNamespace b = val){return Days::Tuesday;}

}

using namespace std;

#define UseNamespace(ns, x) do {using namespace ns; x;} while (0)

int main()
{
    //with a macro
    UseNamespace(hidden,hidden::foo(Monday, Tuesday));

    {
    //foo is found by argument dependent lookup
    using hidden::Days;
    foo(Days::Monday,Days::Tuesday);
    }

    {
    using r = hidden::RegisterNamespace;
    //foo and GetMonday / GetTuesday are all found by argument dependent lookup
    foo(GetMonday(r::val),GetTuesday(r::val));
    }

    return 0;
}
于 2013-12-23T16:26:13.587 に答える