Special behavior for decltype of call operator for incomplete types

白昼怎懂夜的黑 提交于 2019-12-13 11:47:38

问题


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

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.

This compiles:

#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;
}

The error I get:

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

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?

EDIT: Made an even bigger simplification

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


回答1:


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

Now consider [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;

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.

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]:

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]).

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 [...]

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



来源:https://stackoverflow.com/questions/45518910/special-behavior-for-decltype-of-call-operator-for-incomplete-types

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!