Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to understand the proposed resolution of #1664

After looking at the proposed resolution of #1664(proposed resolution 1664), I'm confused for the rules of a default argument of a function template, Cite the content here:

According to 8.1.5 [expr.prim.lambda] paragraph 3

The closure type is declared in the smallest block scope, class scope, or namespace scope that contains the corresponding lambda-expression. [Note: This determines the set of namespaces and classes associated with the closure type (6.4.2 [basic.lookup.argdep]). The parameter types of a lambda- declarator do not affect these associated namespaces and classes. —end note]

However, 17.8.1 [temp.inst] paragraph 13 says

If a function template f is called in a way that requires a default argument to be used, the dependent names are looked up, the semantics constraints are checked, and the instantiation of any template used in the default argument is done as if the default argument had been an initializer used in a function template specialization with the same scope, the same template parameters and the same access as that of the function template f used at that point.

A possibility, then, is that the closure type for lambda expression in a default argument for a template function (or, presumably, a member function of a class template) is to be considered as having been declared in some block scope in the body of the fictional function template specialization.

Consider the following example:

 namespace J {
    inline namespace K {
      template <typename T> int zap(const T &t) { foo(t); return 0; }
      template <typename T> void zip(int = zap([] { })) { }
    }
    template <typename T> void foo(const T &) { }
  }
  void bar() { 
    J::K::zip<long>(); 
    /*Accroding to the above wording,the invoke just like:  
      => J::K::zip<long>(zap([] { })); 
   */
  }

If zip were not a template, argument-dependent lookup successfully resolves the lookup for foo in all implementations tested; however, there is implementation variance in the handling of the example as written.

Proposed resolution (September 2013):

Change 17.8.1 [temp.inst] paragraph 13 as follows:
If a function template f is called in a way that requires a default argument to be used, the dependent names are looked up, the semantics constraints are checked, and the instantiation of any template used in the default argument is done as if the default argument had been an initializer used in a function template specialization with the same scope, the same template parameters and the same access as that of the function template f used at that point, except that the scope in which a closure type is declared (8.1.5 [expr.prim.lambda]) — and therefore its associated namespaces — remain as determined from the context of the definition for the default argument. This analysis is called default argument instantiation. The instantiated default argument is then used as the argument of f.

Note the emphasized part,if I do not misunderstand,It means if the emphasized part comment out,the foo could not be looked up by argument dependent look up because the argument [] { } which namespace is neither J nor K,assume the form within function bar like J::K::zip<long>(zap([] { }) /*default argument*/);,So accroding to [expr.prim.lambda] paragraph 3 the namespace of [] { } is at fuction bar and at that scope,there's no foo can be found,So the emphasized part is for this case that consider the namespace of [] { } within zap as the same as zap,it means the namespace of [] { } is K,Now the foo can be found in the parent namespace J by argument dependent lookup rules ,So far ,If i misundertand these rules,please correct me.the other point view is that the default argument is evaluated every time when the function called,even if the default is non-dependent.So continue consider the following code:

#include <iostream>
struct A {

};
template<typename T>
int func(T, float) {  //#a
    std::cout << "float" << std::endl;
    return 0;
}
template<typename T>
void test(int = func(A{}, 0)) { //#1

}
template<typename T>
int func(T, int) {  //#b
    std::cout << "int" << std::endl;
    return 0;
}
int main() {
    test<A>(); //#2 transform to: test<A>(func(A{}, 0)); here,#b should be the best match
    std::getchar();
}

Although the default argument func is non-dependent,However it should be determined every time when the function test is called and I test the code in some compliers.
All the version of MSVC report "int",gcc report "float",clang report "float",what's the hell?Accroding to the report of gcc or clang,It seems to the func is determined at #1and MSVC proved that the func is determined at #2. if MSVC is wrong ,that means the non-dependent default argument could be determined within #1 and it's unnecessary to determine every time when the function called,why the emphasized part need to add?(If I understand the emphasized part correctly,the purpose of it is that keep the namespace of closure type within default argument consistent whether the lambda-expression is at the point of function declaration or the point of call). If I misunderstand these rules,How to interpret them correctly?

UPDATE:

the version 9.1 or higher of gcc can not complie the code mentioned in #1664,it will report error (the complie result)

Questions:

1.Does the non-dependent default argument of function template or non-template function need to determined every time when the corresponding function called?

2.what does "the definition for the default argument" mean?Is this wording strictly?(In other word,my understanding is,what actullay the added rules want to express is that the namespace of closeure type is that of a function declartion where contain a default argument which contain the corresponding lambda-expression,Right?if my understanding about this is wrong,correct me)

like image 202
xmh0511 Avatar asked Mar 15 '20 08:03

xmh0511


People also ask

How do you write a resolution example?

The title of the resolution must appropriately reflect the intent. Resolutions begin with "Whereas" statements, which provides the basic facts and reasons for the resolution, and conclude with "Resolved" statements which, identifies the specific proposal for the requestor's course of action.

What is a resolution example?

Here is an example of a conclusion versus a resolution: Resolution: The team happily celebrated their victory after a challenging face-off with their rival. Here, the resolution marks the end of a story.


1 Answers

Default arguments are evaluated every time they are called, but this is a runtime property: calls are counted not by source line but by actual control flow. Separately, a default argument for a templated function is considered to be a definition and is instantiated when needed, up to once per specialization of the function (with the usual proviso about multiple points of instantiation that must agree). CWG1664 was a very narrow issue based on how that instantiation was worded: by introducing a fictitious function template, it left open the possibility that the lambda’s declaration “physically” moved. The fix really does affect only ADL.

Your func example instead illustrates the usual name-lookup rules in templates: no matter how many times and whence test’s default argument is instantiated, func in it is not a dependent name and thus finds func(T,float) (every time). MSVC has famously never implemented this rule correctly (because, to be fair, their implementation predates the rule and they’ve only recently started the requisite (and nearly complete) rewrite of their template support).

Meanwhile, recent GCC is plainly buggy with the CWG1664 example: note that it complains about foo being used but not defined, contradicting both the plainly visible { } and its prior error message about not finding it.

like image 130
Davis Herring Avatar answered Oct 19 '22 11:10

Davis Herring