定制点 - 名称空间注入

时间:2018-01-04 13:44:00

标签: c++ visual-studio-2017 overloading

首先是冗长的解释,第二个是实际的问题:

在C ++库中,我想提供自定义点。也就是说,某些方法可以注入"由用户。通常,这是通过ADL以下列方式完成的:

文件operators.h包含:

namespace operators
{
    namespace print_overloads
    {
        void print_value(double x)
        {
            cout << x << endl;
        }
    }

    namespace detail
    {
        template <typename Value>
        void adl_print(Value x)
        {
            using print_overloads::print_value;
            print_value(x);
        }
    }

    template <typename Value>
    void print(Value x)
    {
        detail::adl_print(x);
    }
}

print_value()通过ADL提供自定义点。要使用它,可能会有testi.cpp

#include "operator.h"
namespace custom
{
    struct A {};

    void print_value(A)
    {
        cout << "ADL A overload" << endl;
    }
}


int main()
{
    operators::print(custom::A{});
}

这是按预期工作的。但是,仅当用户可以在相应的命名空间中定义函数时才适用(在这种情况下为namespace custom)。

我的想法是在上面的示例中引入一个专用的重载命名空间namespace print_overload。对于用户,这应该允许:

#include "operators.h"
namespace custom_inaccessible
{
    struct A {};
}

namespace operators::print_overloads
{
    void print_value(custom_inaccessible::A)
    {
        cout << "A overload" << endl;
    }
}


int main()
{
    operators::print(custom_inaccessible::A{});

    int pause;
    std::cin >> pause;
    return 0;
}

不幸的是,这不起作用。当前的Microsoft Visual Studio 2017 C ++编译器失败并显示:

error C2664: 'void operators::print_overloads::print_value(double)': cannot convert argument 1 from 'testi::B' to 'double'

似乎在重载列表中没有考虑print_value(A)的重载。经过一些修补,我发现一致性模式设置设置为Yes(/permissive-)。如果我将它设置为No,一切正常。

现在提出问题:

  • 此行为是否在标准中定义?
  • 如果是,标准的哪一部分对此结果负责?
  • 或者它是Visual Studio一致性模式的一些错误吗?

1 个答案:

答案 0 :(得分:1)

要回答有关标准的问题,相关段落位于[temp.dep.candidate]

  

对于postfix-expression是从属名称的函数调用,   使用通常的查找规则找到候选函数   ([basic.lookup.unqual],[basic.lookup.argdep])除了:

     
      
  • 对于使用非限定名称查找的查找部分,只能找到模板定义上下文中的函数声明。
  •   
  • 对于使用关联命名空间([basic.lookup.argdep])查找的部分,只能在任一个中找到函数声明   模板定义上下文或模板实例化上下文   找到了。
  •   

上述意味着在using print_overloads::print_value;(根据第一个项目符号的非限定名称查找)中找到的任何内容必须已经存在于定义模板的位置。用户不能只是重新打开命名空间并在之后添加它。

MSVC在一致性模式下拒绝它是非常正确的。

详细说明,根据[namespace.udecl]/1

,using声明通过限定名称查找引入名称
  

using声明中的每个using-declarator都引入了一组   声明进入声明区域   出现使用声明。由...引入的声明集   通过执行限定名称查找来找到using-declarator   using-declarator中的名称,不包括隐藏的函数   如下所述。

但是,using声明只引入了非依赖名称。根据{{​​3}}:

  

模板定义中使用的非依赖名称可以使用   通常的名称查找和绑定在他们使用的点。

“使用它们的点”是模板定义的重点。