参数依赖查找的反转的解决方法?

时间:2013-12-15 22:04:27

标签: c++ c++11 enums namespaces argument-dependent-lookup

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,此限制尤其令人讨厌。我想知道是否有一种模拟此功能的技术(C ++ 11也可以)。假代码:

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

特别是如果有enum的解决方法。

此代码说明了问题:

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因为SaturdaySunday等不能直接在范围内(实际上using longname::days::_会给我一个编译错误)

1 个答案:

答案 0 :(得分:3)

是和否。绝大多数没有。

坏消息是,如果枚举超出当前范围,例如Tuesday等,则无法将其传递给函数,即使该函数是在枚举可见的命名空间中声明的。这是因为参数查找在您编写函数调用时首先发生,并且参数无法传递给gun,然后发生名称查找。没有什么可以改变这一点 - 但也有好消息。

首先,您似乎需要映射ns::foo(arg1, arg2)的行为 - > {using namespace ns; ns::foo(arg1, arg2);}。函数调用和模板不能改变这一点,但宏可以和我包括和示例。

此外,我还提供了参数依赖查找的基本示例。您可以看到,使用此机制可以找到超出范围的函数GetMonday和GetTuesday(返回您的超出范围的枚举),因为您从该命名空间中包含了一种类型。当编译器试图找到GetMonday时,RegisterNamespace::val将隐藏的命名空间添加到范围,GetMonday返回Days,允许编译器找到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;
}