不完整类型的调用运算符的decltype的特殊行为 [英] Special behavior for decltype of call operator for incomplete types

查看:124
本文介绍了不完整类型的调用运算符的decltype的特殊行为的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我一直在努力解决编译问题,并且能够将问题缩小到一小段代码.

I've been struggling with a compilation issue, and have been able to shrink the problem down to a small code segment.

要设置阶段,我正在尝试做CRTP,其中基本方法在派生类中调用另一个方法.复杂的是,我想使用尾随返回类型直接将转发类型转换为Derived类的方法.除非我将转发给派生类中的调用运算符,否则始终无法编译.

To set the stage, I'm trying to do CRTP, where the base method calls another in the derived class. The complication is, I want to use trailing return types to get the type of forwarding directly to the the Derived class's method. This always fails to compile unless I forward to the call operator in the derived 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;
}

这不是:(请注意注释,唯一的区别)

While this does not: (note comment for the only difference)

#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&&>()...));
                     ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~^

在派生类中的decltype解析期间似乎发生了一些特殊的行为.标准中有什么可以解释这一点吗?

There seems to be some special behavior going on during resolution of the decltype in the Derived class. Is there something in the standard that would explain this?

进行了更大的简化

PS:关于Godbolt的编译示例: https://godbolt.org/g/St2gYC

PS: compiling example on godbolt: https://godbolt.org/g/St2gYC

推荐答案

实例化一个类模板会实例化其成员函数模板的声明(

Instantiating a class template instantiates the declarations of its member function templates ([temp.inst]/2). I.e. we're looking at the declaration

template <typename... Args>
auto entry(Args&&... args)
    -> decltype(std::declval<Incomplete&>().operator()(std::declval<Args&&>()...));

现在考虑 [temp.res]/10 :

Now consider [temp.res]/10:

如果名称不依赖模板参数(如14.6.2中定义),则声明(或声明集) 该名称应在该名称在模板定义中出现的位置的范围内;

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()不依赖于模板参数.它既不依赖类型也不依赖于值,也不依赖于名称.显然,范围内没有声明,因此声明格式错误,不需要诊断.

And indeed, the name operator() does not depend on a template parameter. It's neither type- nor value-dependent, and it's not a dependent name, either. Clearly, there is no declaration in scope, hence the declaration is ill-formed, no diagnostic required.

相比之下,您的第一个代码段无需在Incomplete中查找名称.只有在x—

By contrast, your first snippet does not necessitate the lookup of a name within Incomplete. The transformation of x(...), where x is of class type, to x.operator()(...) happens only after operator() is looked up within x—[over.call]:

因此,呼叫x(arg1,...)被解释为x.operator()(arg1,...) 如果存在T​::​operator()(T1, T2, T3)且类型为T 的类对象x 如果运算符被重载选择为最佳匹配函数 解决机制([over.match.best]).

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]中找到对此解释的进一步确认:

This is different from the paragraph that made your second code ill-formed: [temp.res]/10 says that some declaration(s) must be in scope, and that the name is bound to those declarations. The above transformation requires that the argument types (and also the number...) are known such that we can uniquely determine one operator() to be called; that is, we don't just insert .operator(), but always simultaneously identify which operator function is called. We can find further confirmation of this interpretation in [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()的参数操作数显然是类型相关的.

The operator()'s argument operands are clearly type-dependent.

这篇关于不完整类型的调用运算符的decltype的特殊行为的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆