Welcome to OStack Knowledge Sharing Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
185 views
in Technique[技术] by (71.8m points)

c++ - What exactly is the "immediate context" mentioned in the C++11 Standard for which SFINAE applies?

Paragraph 14.8.2/8 of the C++11 Standard specifies the conditions under which a substitution failure shall or shall not result in a "hard" compilation error (thereby causing compilation to fail) or in a "soft" error which would just cause the compiler to discard a template from a set of candidates for overload resolution (without making compilation fail and enabling the well-known SFINAE idiom):

If a substitution results in an invalid type or expression, type deduction fails. An invalid type or expression is one that would be ill-formed if written using the substituted arguments. [ Note: Access checking is done as part of the substitution process. —end note ] Only invalid types and expressions in the immediate context of the function type and its template parameter types can result in a deduction failure. [...]

The words "immediate context" appear only 8 times in the whole C++11 Standard, and each time they are followed by (or occur as part of) an instance of the following (non-normative) text:

[Note: The evaluation of the substituted types and expressions can result in side effects such as the instantiation of class template specializations and/or function template specializations, the generation of implicitly-defined functions, etc. Such side effects are not in the “immediate context” and can result in the program being ill-formed.—end note ]

The note gives a (not very generous) hint on what is meant by immediate context, but at least for me this is often not enough to decide whether a substitution is or is not supposed to cause a "hard" compilation error.

QUESTION:

Could you provide an explanation, a decision procedure, and/or some concrete examples to help figuring out in what cases a substitution error does and does not occur in the "immediate context" of the function type and its template parameter types?

Question&Answers:os

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Answer

0 votes
by (71.8m points)

If you consider all the templates and implicitly-defined functions that are needed to determine the result of the template argument substitution, and imagine they are generated first, before substitution starts, then any errors occurring in that first step are not in the immediate context, and result in hard errors.

If all those instantiations and implicitly-definitions (which might include defining functions as deleted) can be done without error, then any further "errors" that occur during substitution (i.e. while referring to the instantiated templates and implicitly-defined functions in the function template's signature) are not errors, but result in deduction failures.

So given a function template like this:

template<typename T>
void
func(typename T::type* arg);

and a "fall-back" that will be used if deduction fails for the other function:

template<typename>
void
func(...);

and a class template like this:

template<typename T>
  struct A
  {
    typedef T* type;
  };

A call to func<A<int&>>(nullptr) will substitute A<int&> for T and in order to check if T::type exists it must instantiate A<int&>. If we imagine putting an explicit instantiation before the call to func<A<int&>(nullptr):

template class A<int&>;

then that would fail, because it tries to create the type int&* and pointers to references are not allowed. We don't get to the point of checking if substitution succeeds, because there is a hard error from instantiating A<int&>.

Now let's say there's an explicit specialization of A:

template<>
  struct A<char>
  {
  };

A call to func<A<char>>(nullptr) requires the instantiation of A<char>, so imagine an explicit instantiation somewhere in the program before the call:

template class A<char>;

This instantiation is OK, there's no error from this, so we proceed to argument substitution. The instantiation of A<char> worked, but A<char>::type doesn't exist, but that's OK because it's only referenced in the declaration of func, so only causes argument deduction to fail, and the fall-back ... function gets called instead.

In other situations substitution might cause special member functions to be implicitly-defined, possibly as deleted, which might trigger other instantiations or implicit definitions. If errors occur during that "generating instantiations and implicit definitions" stage then they're errors, but if that succeeds but during substitution an expression in the function template signature turns out to be invalid e.g. because it uses a member that doesn't exist or something that got implicitly defined as deleted, that's not an error, just a deduction failure.

So the mental model I use is that substitution needs to do a "preparation" step first to generate types and members, which might cause hard errors, but once we have all the necessary generation done, any further invalid uses are not errors. Of course all this does is move the problem from "what does immediate context mean?" to "Which types and members need to be generated before this substitution can be checked?" so it may or may not help you!


与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome to OStack Knowledge Sharing Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...