C++ 定制点-名称空间注入

C++ 定制点-名称空间注入,c++,visual-studio-2017,overloading,C++,Visual Studio 2017,Overloading,首先是冗长的解释,其次是实际问题: 在C++库中,我想提供定制点。也就是说,用户可以“注入”某些方法。通常,这是通过ADL以以下方式完成的: 文件operators.h包含: namespace operators { namespace print_overloads { void print_value(double x) { cout << x << endl; } }

首先是冗长的解释,其次是实际问题:

在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);
    }
}
这正是我们想要的。但是,仅当用户可以在相应的命名空间中定义函数时(在本例中为
namespace custom

我的想法是在上面的示例中引入一个专用的重载名称空间,
namespace print\u-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;
}
#包括“operators.h”
命名空间自定义\u不可访问
{
结构A{};
}
名称空间运算符::print_重载
{
无效打印值(自定义打印不可访问::A)
{
不能停顿;
返回0;
}
遗憾的是,这不起作用。当前微软Visual Studio 2017 C++编译器失败了:

错误C2664:'void运算符::print_重载::print_值(double)':无法将参数1从'testi::B'转换为'double'

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

现在回答问题:

  • 该行为是否在标准中定义
  • 如果是,标准的哪一部分对该结果负责
  • 或者它是VisualStudio一致性模式的一些bug

要回答您对标准的疑问,请访问以下相关段落:

对于后缀表达式为从属名称的函数调用, 候选函数是使用常用的查找规则找到的 ([basic.lookup.unqual],[basic.lookup.argdep]),除了:

  • 对于使用非限定名称查找的查找部分,只找到模板定义上下文中的函数声明
  • 对于使用关联名称空间([basic.lookup.argdep])的查找部分,在 模板定义上下文或模板实例化上下文 找到了
上面的意思是,由于使用print\u重载::print\u value;(根据第一个项目符号,非限定名称查找)而在定义模板时发现的任何内容都必须已经存在。用户不能只是重新打开名称空间,然后再添加它

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


为了详细说明,using声明通过限定名称查找引入名称,根据:

using声明中的每个using声明符都引入了一组 声明进入声明性区域,其中 将显示using声明。由 通过对 使用声明器中的名称,不包括隐藏为 如下所述

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

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


“使用点”作为模板定义的要点。

尝试gcc和clang的代码。但看起来一致性模式设置的行为正确,不一致性模式不正确,因为在解析函数调用时只考虑以前声明的函数,MSVC直到最近才对模板出错。I我会说使用ADL做这件事是很不寻常的。最好引入一些用户可以专门化的类型特征,或者接受一些带有默认值的额外模板参数。@VTT我指的是:我看不出
使用print\u重载::print\u value;
是一种不合格的名称查找。根据,“对于非限定名称,即不显示在作用域解析运算符右侧的名称::”,这应该是限定名称查找?@LcdDrm-
print_value(x);
是非限定名称查找(对于
print_value
)。必须事先声明使用声明提供给它的名称。
#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;
}