自定义点 - 命名空间注入
Point of customization - namespace injection
首先是冗长的解释,其次是实际问题:
在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:
模板定义中使用的非依赖名称使用 通常的名称查找并在使用它们时绑定。
"使用它们的点"是模板定义的点。
- 命名空间中具有.h和.cpp文件的类
- 从父命名空间重载类型
- 在命名空间中定义函数还是限定函数
- C++:对不存在的命名空间使用命名空间指令
- 通过继承类使用来自不同命名空间的运算符
- 使用命名空间时出现多个定义错误
- CUDA内核和数学函数的显式命名空间
- 嵌套的匿名命名空间
- CMakeLists.txt中的命名空间表示法
- 类是C++中的命名空间吗
- 在命名空间中使用全局命名空间中的函数
- 如何使 std::sort 在 std::swap 和我的命名空间的模板化交换之间没有名称冲突?
- '使用命名空间{嵌套在另一个命名空间中的某个命名空间}"
- 是否可以将函数导入命名空间,但不能导出它?
- 为什么在这种特殊情况下不需要将 std::hash() 的专用化注入到 std 命名空间中?
- 作用域中定义的变量与注入该作用域的命名空间变量
- 自定义点 - 命名空间注入
- C库设施标头的命名空间注入不一致
- 将实验命名空间注入 std
- 如果在全局命名空间中的类中引入一个朋友,则该朋友将被全局注入