c++ - 不完整类型的调用运算符的 decltype 的特殊行为

标签 c++ templates operator-overloading language-lawyer name-lookup

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

为了做好准备,我正在尝试执行 CRTP,其中基方法调用派生类中的另一个方法。复杂的是,我想使用尾随返回类型来直接获取转发到派生类方法的类型。这总是无法编译除非我转发给派生类中的调用运算符。

编译:

#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 类的 decltype 解析过程中似乎有一些特殊的行为发生。标准中有什么可以解释这一点的吗?

编辑:做了更大的简化

PS:godbolt 上的编译示例:https://godbolt.org/g/St2gYC

最佳答案

实例化类模板会实例化其成员函数模板 ([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 是类类型,到 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 表示某些声明 必须在范围内,并且名称是绑定(bind)的那些声明。上述转换要求参数类型(以及数量...)是已知的,以便我们可以唯一确定要调用的 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() 的参数操作数显然是依赖于类型的。

关于c++ - 不完整类型的调用运算符的 decltype 的特殊行为,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/45518910/

相关文章:

javascript - extjs 4.2.1 GridPanel - 在模板列中显示/隐藏链接

c++ 使用 "*"运算符没有运算符匹配这些操作数

c++ - 这是什么意思?

c++ - 非静态成员引用的二维数组问题

jQuery 模板引擎

c++ - #include<iostream> 存在,但我收到错误 : identifier "cout" is undefined. 为什么?

c++ - C++中带有模板变量的结构

c++ - boost::apply_visitor 不是 [some] 类的成员

C++:静态分析代码(和/或预处理代码)的工具

c++ - 为什么 null std::optional 被认为小于任何值,而不是更多