gpt4 book ai didi

c++ - 命名空间导致次优模板重载解析

转载 作者:搜寻专家 更新时间:2023-10-31 01:38:48 25 4
gpt4 key购买 nike

这与 this question 非常相似,但我不确定那里的答案是否完全适用于我放在一起演示问题的最少代码。 (我的代码使用尾随返回类型,并且还有一些其他差异。)此外,似乎没有解决 MSVC 的行为是否合法的问题。

简而言之,当函数模板位于命名空间内时,我看到编译器选择通用函数模板实例化而不是更具体的重载。

考虑以下一组命名空间和类定义:

namespace DoStuffUtilNamespace
{
template<typename UNKNOWN>
void doStuff(UNKNOWN& foo)
{
static_assert(sizeof(UNKNOWN) == -1, "CANNOT USE DEFAULT INSTANTIATION!");
}
}

class UtilForDoingStuff
{
public:
template <typename UNKNOWN>
void doStuffWithObjectRef(UNKNOWN& ref)
{
DoStuffUtilNamespace::doStuff(ref);
}
};

class MyClassThatCanDoStuff { };

namespace DoStuffUtilNamespace
{
using ::MyClassThatCanDoStuff; // No effect.

void doStuff(MyClassThatCanDoStuff& foo) { /* No assertion! */ }
}

...以及以下用例:

int main()
{
MyClassThatCanDoStuff foo;
DoStuffUtilNamespace::MyClassThatCanDoStuff scoped_foo;
UtilForDoingStuff util;

DoStuffUtilNamespace::doStuff(foo); // Compiles
DoStuffUtilNamespace::doStuff(scoped_foo); // Compiles
util.doStuffWithObjectRef(foo); // Triggers static assert
util.doStuffWithObjectRef(scoped_foo); // Triggers static assert
}

如果整个DoStuffUtilNamespace被删除并且它的所有成员都被移动到全局范围,这可以很好地使用 G++ 和 Clang++ 进行编译。

使用命名空间,doStuff当然是从属名称。根据top-voted answer on the similar question ,标准说:

In resolving dependent names, names from the following sources are considered:

  • Declarations that are visible at the point of definition of the template.

  • Declarations from namespaces associated with the types of the function arguments both from the instantiation context and from the definition context.

这对我来说似乎有点奇怪;我不明白为什么第一个要点会指定声明必须在模板的定义点而不是在实例化点可见,因为第二个要点明确指定某些声明仅在实例化时可见是允许的。 (如果有人愿意提供理由,我将不胜感激,但这不是我的问题,因为我的理解是“标准委员会为什么决定 X”这种形式的问题不在主题之列。)

所以我认为这解释了为什么 util.doStuffWithObjectRef(foo);触发静态断言:doStuff(MyClassThatCanDoStuff&)尚未在 UtilForDoingStuff::doStuffWithObjectRef<UNKNOWN>(UNKNOWN&) 的定义点声明.确实移动了class UtilForDoingStuffdoStuff 之后定义 重载已被定义似乎解决了这个问题。

但是标准中所说的“与函数参数类型相关联的命名空间”到底是什么意思?不应该 using ::MyClassThatCanDoStuff声明,以及 scoped_foo 的明确范围命名空间中的实例类型,触发依赖于参数的查找,并且此查找不应该找到 doStuff() 的非断言定义吗? ?

另外,整个代码使用clang++ -ftemplate-delayed-parsing编译没有错误,它模拟 MSVC 的模板解析行为。这似乎更可取,至少在这种特殊情况下,因为随时向 namespace 添加新声明的能力是 namespace 的主要吸引力之一。但是,如上所述,根据标准,它似乎并不完全符合法律条文。这是允许的,还是不符合规定的情况?

EDIT:: 正如 KIIV 所指出的,有一个解决方法;如果使用模板特化而不是重载,代码将编译。我仍然想知道有关该标准的问题的答案。

最佳答案

With the namespace, doStuff is of course a dependent name.

您的出发点是错误的。对于像 DoStuffUtilNamespace::doStuff(ref) 这样的限定调用,没有 ADL。 [basic.lookup.argdep]/p1,强调我的:

When the postfix-expression in a function call (5.2.2) is an unqualified-id, other namespaces not considered during the usual unqualified lookup (3.4.1) may be searched, and in those namespaces, namespace-scope friend function or function template declarations (11.3) not otherwise visible may be found.

DoStuffUtilNamespace::doStuff 是一个qualified-id,而不是一个unqualified-id。 ADL 不适用。

因此,DoStuffUtilNamespace::doStuff 也不是依赖名称。 [临时部门]/p1:

In an expression of the form:

postfix-expression ( expression-listopt)

postfix-expression 是一个unqualified-idunqualified-id 表示一个从属名称 if [...]。如果运算符的操作数是依赖于类型的表达式,则该运算符还表示 依赖名称。这些名称是未绑定(bind)的,可以在 模板实例化点(14.6.4.1)在这两个上下文中 模板定义和实例化点的上下文

(相关名称 的斜体表示该段定义了术语。)

相反,根据 [temp.nondep]/p1:

Non-dependent names used in a template definition are found using the usual name lookup and bound at the point they are used.

找不到您稍后的重载声明。


特化之所以有效,是因为它仍然使用相同的函数模板声明;您只是提供了与默认实现不同的实现。


But what exactly does the standard mean by "namespaces associated with the types of the function arguments"? Shouldn't the using ::MyClassThatCanDoStuff declaration, together with the explicit scoping of the scoped_foo instance type within the namespace, trigger argument-dependent lookup

没有。 using-declaration 不会影响 ADL。 [basic.lookup.argdep]/p2,强调我的:

For each argument type T in the function call, there is a set of zero or more associated namespaces and a set of zero or more associated classes to be considered. The sets of namespaces and classes is determined entirely by the types of the function arguments (and the namespace of any template template argument). Typedef names and using-declarations used to specify the types do not contribute to this set. The sets of namespaces and classes are determined in the following way:

  • If T is a fundamental type, [...]

  • If T is a class type (including unions), its associated classes are: the class itself; the class of which it is a member, if any; and its direct and indirect base classes. Its associated namespaces are the innermost enclosing namespaces of its associated classes. Furthermore, if T is a class template specialization, its associated namespaces and classes also include: the namespaces and classes associated with the types of the template arguments provided for template type parameters (excluding template template parameters); the namespaces of which any template template arguments are members; and the classes of which any member templates used as template template arguments are members. [ Note: Non-type template arguments do not contribute to the set of associated namespaces. —end note ]

  • [...]

关于c++ - 命名空间导致次优模板重载解析,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/32235765/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com