不完整类型

时间:2017-08-05 06:15:41

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

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

要设置阶段,我正在尝试执行CRTP,其中基本方法在派生类中调用另一个。复杂的是,我想使用尾随返回类型直接获取转发类型到Derived类的方法。这总是无法编译,除非我转发到派生类中的调用运算符。

编译:

#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

1 个答案:

答案 0 :(得分:4)

实例化类模板实例化其成员函数模板([temp.inst]/2)的声明。即我们正在查看声明

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

现在考虑[temp.res]/10

  

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

实际上,名称operator()不依赖于模板参数。它既不依赖于类型也不依赖于价值,也不是依赖名称。显然,范围内没有声明,因此声明格式不正确,无需诊断。

相比之下,您的第一个代码段无需在Incomplete中查找名称。将x(...)类型x转换为x.operator()(...)的转换只有在x [over.call]内查找operator()后才会发生:

  

因此,调用x(arg1,...)被解释为x.operator()(arg1,...)   如果存在T​::​operator()(T1, T2, T3),则类型为T 的类对象x   如果操作员被过载选为最佳匹配函数   解决机制([over.match.best])。

这与使你的第二个代码格式错误的段落有所不同:[temp.res] / 10表示某些声明必须在范围内,并且名称是绑定的那些宣言。上述转换要求参数类型(以及数字......)是已知的,以便我们可以唯一地确定要调用的一个operator();也就是说,我们不只是插入.operator(),而是始终同时识别调用哪个运算符函数。我们可以在[temp.dep]中找到对此解释的进一步确认:

  

如果运算符的操作数是类型相关的表达式,则运算符也表示从属名称。这些名称是未绑定的,并且在模板实例化[...]

时查找

operator()的参数操作数显然与类型有关。