Though, the question is whether there is any part of the standard (C++11) that renders the technique explained ill-formed, or to suffer from undefined-behavior?
Filip Roséen
A friend function defined in a class template is treated as if it were defined at the point of instantiation of the specialization ([temp.inject] §14.6.5). The point of instantiation is retroactively before any non-template function declaration or definition which incurred the chain of instantiations leading to the friend, if the outermost instantiation was a function ([temp.point] 14.6.4.1; this distinction is arbitrary but significant in this context).The existence of such a friend may be observed after the point of instantiation, but before the specialization has been mentioned within the function. This does not violate [temp.dep.candidate] 14.6.4.2 because there are not two valid specializations in two instantiation contexts, but only one correct interpretation which is impossible because the initial instantiation context depends on things at the point of instantiation which do not yet exist.constexpr int get();template< int value >class set { // Instantiate this exactly once.friend constexpr int get() { return value; }};// Point of instantiation is here.int main() {char a[ get() ]; // OK but impossible: get() was already defined at the POI.set< 5 > b;char c[ get() ]; // This is also well-defined, and it actually works.}
On 2015–04–27, at 9:28 AM, Richard Smith <ric...@metafoo.co.uk> wrote:Another natural way might be to say that *any* substitution (including template parameters, default arguments, etc) must give the same results at all points of substitution, not just template instantiations.
(1)
denotes in what context the compiler may generate the relevant code. What triggers the instantiation is where the change actually takes effect ((2)
and (3)
)."On 2015–04–27, at 12:28 PM, Filip Roséen <filip....@gmail.com> wrote:Hmm, I am not sure I agree with your wording related to the point of instantiation,
because the explicit specialization is not there until we have actually seen the (implicit) instantiation happen; that's my take on the relevant sections of the Standard.
14.6.5p1 (n3337) says "when a template is instantiated", in my book this (and other sections) means that such state cannot leak out prior to the instantiation.
The intent is that it is a permissible implementation technique to do template instantiation at the end of a translation unit rather than at an actual point of instantiation. This idea is not reflected in the current rules, however.
This is addressed in the blog post as; "It is important to note that a point of instantation(1)
denotes in what context the compiler may generate the relevant code. What triggers the instantiation is where the change actually takes effect ((2)
and(3)
).”
--
On 2015–04–27, at 1:53 PM, Filip Roséen <filip....@gmail.com> wrote:> 1. Your utility won’t work from inside a template. It must always be used from a non-template context, because a second instantiation of the same template would see the friends generated by the first instantiation. Every specialization that is used at all has at least two points of instantiation, including one at the end of the TU. (In practice, though, you will probably get consistent behavior for templates with only exactly two PoI’s.)Unless they rely on different friends, so the above is not really an issue,
also; class template specializations has at most one point of instantiation - not two, that only applies to function templates.
> The problem is that the standard doesn’t describe an order for the instantiations to happen.Unless we are talking dependency, where it must happen in some order - this does not only include literally dependent types. The example in the current post is not sufficient enough to explain this in detail, but an explanation will be included in upcoming posts (there are more neat little tricks to be published).
> Your utility requires the point instantiation of the friend function to be before the point of use. Otherwise, it wouldn’t be in scope. However, there is no point of instantiation defined for friends at all. You’re relying on friends being treated like classes, not like other functions, which seems especially brittle.This is not entirely true, it requires the point of instantiation of the class-template specialization to be where it needs to be;
func (short)
and func (float)
are undefined until A<short>
and A<float>
have been instantiated,
respectively.” This is not a safe assumption, because the point of instantiation may render the definition invisible to the current function.please note that default template-arguments are not part of the specialization itself, that means that `sizeof(...)` used as a default-argument to the template function effectively puts the instantiation of the relevant class prior to `main` (not prior to the instantiation of the function template specialization). The post "abuses" the instantiation of class template specializations, not function template specializations.
From: David Krauss Sent: Monday, April 27, 2015 8:35 AM Reply To: std-dis...@isocpp.org Subject: Re: [std-discussion] non-constant constant-expressions, is it really possible? |
On 2015–04–27, at 2:57 PM, Filip Roséen <filip....@gmail.com> wrote:No, two different specializations of the same template (of course).
I think you are focusing on the wrong part; the instantiation order of the functions has nothing to do with the template default-arguments (which effectively instantiates the class template).. the template default-arguments are what is important.
In reply to the rest: the code is confirming, no edit is pending, there are cases where the order of function template instantiation matters - but the example provided is not one of them.
It is important to note that constexpr functions follow the same rules of normal functions,
and just to make this even more clear one could also have expressed the method in the post by "abusing" default-arguments (non-template, regular arguments) since these expressions are said to be evaluated each time the function is invoked.
The use of a template instead of a non-template is simply because the upcoming examples will be working with a similar concept (since that is shorter, but regular function default-arguments can accomplish the same thing).
From: David Krauss Sent: Monday, April 27, 2015 10:29 AM Reply To: std-dis...@isocpp.org Subject: Re: [std-discussion] non-constant constant-expressions, is it really possible? |
On 2015–04–27, at 4:44 PM, Filip Roséen <filip....@gmail.com> wrote:No, the "second" instantiation will use the template-parameters that
I hope the above explains what I mean, I'm currently on the subway and I get distracted quite easily.
I've also implemented a compile-time counter, and jftr everything the post (that this thread is about) talks about has already been implemented (and compiles with gcc, clang, msvc, and "somewhat" in ICC (because of the lack of proper support for constexpr in ICC).
On 2015–04–27, at 4:44 PM, Filip Roséen <filip....@gmail.com> wrote:No, the "second" instantiation will use the template-parameters thatAgain, I’m not talking about the template f() or any other part of your utility. I’m talking about any function template which *uses* your utility.The problem is absent from your examples, but you should still mention that your utility can’t be used within a (function) template.
I hope the above explains what I mean, I'm currently on the subway and I get distracted quite easily.I understand your program perfectly well. I’ve used all these tricks before, except for using noexcept to detect constant expressions. I’m also familiar with the frustration inherent in this area, the excitement and the disappointment. If you have a lot of StackOverflow reputation, you can even see a friend-based compile-time counter that I posted before arriving at the final answer linked earlier: http://stackoverflow.com/a/6166338/153285 . (I was fortunate to have Johannes Schaub reviewing my work — we were competing to see who could solve the problem first.)
I've also implemented a compile-time counter, and jftr everything the post (that this thread is about) talks about has already been implemented (and compiles with gcc, clang, msvc, and "somewhat" in ICC (because of the lack of proper support for constexpr in ICC).Unfortunately, implementations are only weakly correlated with the theoretical ideal of the language. There are permissible things that no current implementation does, for example, concurrent processing of statements in a function. There are requirements that aren’t universally implemented, for example evaluating the unevaluated context of noexcept as a constant expression.I hope you will stop to consider how much of the observed behavior is guaranteed by the standard.
To unsubscribe from this group and stop receiving emails from it, send an email to std-discussion+unsubscribe@isocpp.org.
- Core issue 2118 Stateful metaprogramming via friend injection: We will make it ill-formed.
Bad news from the Lenexa CWG minutes: