自定义点 - 命名空间注入

Point of customization - namespace injection

本文关键字:注入 命名空间 自定义      更新时间:2023-10-16

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

在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 Conformance模式的一些错误?

为了回答您关于标准的查询,相关段落位于[temp.dep.candidate ]:

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

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

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

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


为了详细说明,use 声明根据 [namespace.udecl]/1 通过限定名称查找引入名称:

使用声明

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

但是,use 声明只引入了非依赖名称。根据 [temp.nondep]/1:

模板定义中使用的非依赖名称使用 通常的名称查找并在使用它们时绑定。

"使用它们的点"是模板定义的点。