不完整类型的调用运算符的 decltype 的特殊行为
Special behavior for decltype of call operator for incomplete types
我一直在努力解决编译问题,并且能够将问题缩小到一个小代码段。
为了做好准备,我正在尝试做 CRTP,其中基本方法在派生 class 中调用另一个方法。复杂的是,我想使用尾随 return 类型来获取直接转发给 Derived class 方法的类型。这总是无法编译 除非 我转发给派生的调用运算符 class.
这样编译:
#include <utility>
struct Incomplete;
template <typename Blah>
struct Base
{
template <typename... Args>
auto entry(Args&&... args)
-> decltype(std::declval<Blah&>()(std::declval<Args&&>()...));
};
void example()
{
Base<Incomplete> derived;
}
虽然这不是:(注意唯一区别的注释)
#include <utility>
struct Incomplete;
template <typename Blah>
struct Base
{
template <typename... Args>
auto entry(Args&&... args)
-> decltype(std::declval<Blah&>().operator()(std::declval<Args&&>()...));
// I only added this ^^^^^^^^^^^
};
void example()
{
Base<Incomplete> derived;
}
我得到的错误:
<source>: In instantiation of 'struct Base<Incomplete>':
15 : <source>:15:22: required from here
10 : <source>:10:58: error: invalid use of incomplete type 'struct Incomplete'
-> decltype(std::declval<Blah&>().operator()(std::declval<Args&&>()...));
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~^
在解析 Derived class 中的 decltype 期间似乎发生了一些特殊行为。标准中有什么可以解释这一点的吗?
编辑:进行了更大的简化
PS:godbolt 上的编译示例:https://godbolt.org/g/St2gYC
实例化一个class模板会实例化它的成员函数模板([temp.inst]/2)的声明。 IE。我们正在查看声明
template <typename... Args>
auto entry(Args&&... args)
-> decltype(std::declval<Incomplete&>().operator()(std::declval<Args&&>()...));
现在考虑 [temp.res]/10:
If a name does not depend on a template-parameter (as defined in 14.6.2), a declaration (or set of declarations)
for that name shall be in scope at the point where the name appears in the template definition;
事实上,名称 operator()
不依赖于模板参数。它既不依赖类型也不依赖值,也不是依赖名称。显然,范围内没有声明,因此声明格式错误,不需要诊断。
相比之下,您的第一个片段不需要在 Incomplete
中查找名称。 x(...)
,其中 x
是 class 类型,到 x.operator()(...)
的转换仅发生在 operator()
在 x—[over.call] 中查找之后:
Thus, a call x(arg1,...)
is interpreted as x.operator()(arg1, ...) for
a class object x of type T if T::operator()(T1, T2, T3)
exists and
if the operator is selected as the best match function by the overload
resolution mechanism ([over.match.best]).
这与使您的第二个代码格式错误的段落不同:[temp.res]/10 说 某些声明 必须在范围内,并且名称绑定到这些声明。上述转换要求参数类型(以及数量...)是已知的,以便我们可以唯一确定一个 operator()
被调用;也就是说,我们不只是插入 .operator()
,而是始终同时识别调用了哪个运算符函数。我们可以在 [temp.dep]:
中进一步证实这种解释
If an operand of an operator is a type-dependent expression, the operator also denotes a dependent name. Such names are unbound and are looked up at the point of the template instantiation [...]
operator()
的参数操作数显然是类型相关的。
我一直在努力解决编译问题,并且能够将问题缩小到一个小代码段。
为了做好准备,我正在尝试做 CRTP,其中基本方法在派生 class 中调用另一个方法。复杂的是,我想使用尾随 return 类型来获取直接转发给 Derived class 方法的类型。这总是无法编译 除非 我转发给派生的调用运算符 class.
这样编译:
#include <utility>
struct Incomplete;
template <typename Blah>
struct Base
{
template <typename... Args>
auto entry(Args&&... args)
-> decltype(std::declval<Blah&>()(std::declval<Args&&>()...));
};
void example()
{
Base<Incomplete> derived;
}
虽然这不是:(注意唯一区别的注释)
#include <utility>
struct Incomplete;
template <typename Blah>
struct Base
{
template <typename... Args>
auto entry(Args&&... args)
-> decltype(std::declval<Blah&>().operator()(std::declval<Args&&>()...));
// I only added this ^^^^^^^^^^^
};
void example()
{
Base<Incomplete> derived;
}
我得到的错误:
<source>: In instantiation of 'struct Base<Incomplete>':
15 : <source>:15:22: required from here
10 : <source>:10:58: error: invalid use of incomplete type 'struct Incomplete'
-> decltype(std::declval<Blah&>().operator()(std::declval<Args&&>()...));
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~^
在解析 Derived class 中的 decltype 期间似乎发生了一些特殊行为。标准中有什么可以解释这一点的吗?
编辑:进行了更大的简化
PS:godbolt 上的编译示例:https://godbolt.org/g/St2gYC
实例化一个class模板会实例化它的成员函数模板([temp.inst]/2)的声明。 IE。我们正在查看声明
template <typename... Args>
auto entry(Args&&... args)
-> decltype(std::declval<Incomplete&>().operator()(std::declval<Args&&>()...));
现在考虑 [temp.res]/10:
If a name does not depend on a template-parameter (as defined in 14.6.2), a declaration (or set of declarations) for that name shall be in scope at the point where the name appears in the template definition;
事实上,名称 operator()
不依赖于模板参数。它既不依赖类型也不依赖值,也不是依赖名称。显然,范围内没有声明,因此声明格式错误,不需要诊断。
相比之下,您的第一个片段不需要在 Incomplete
中查找名称。 x(...)
,其中 x
是 class 类型,到 x.operator()(...)
的转换仅发生在 operator()
在 x—[over.call] 中查找之后:
Thus, a call
x(arg1,...)
is interpreted as x.operator()(arg1, ...) for a class object x of type T ifT::operator()(T1, T2, T3)
exists and if the operator is selected as the best match function by the overload resolution mechanism ([over.match.best]).
这与使您的第二个代码格式错误的段落不同:[temp.res]/10 说 某些声明 必须在范围内,并且名称绑定到这些声明。上述转换要求参数类型(以及数量...)是已知的,以便我们可以唯一确定一个 operator()
被调用;也就是说,我们不只是插入 .operator()
,而是始终同时识别调用了哪个运算符函数。我们可以在 [temp.dep]:
If an operand of an operator is a type-dependent expression, the operator also denotes a dependent name. Such names are unbound and are looked up at the point of the template instantiation [...]
operator()
的参数操作数显然是类型相关的。