首先是冗长的解释,第二个是实际的问题:
在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
,一切正常。
现在提出问题:
答案 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}}:
模板定义中使用的非依赖名称可以使用 通常的名称查找和绑定在他们使用的点。
“使用它们的点”是模板定义的重点。