Discrepancies with conditional noexcept and overloads

I have a problem that is very similar to this one .

In short, I have a magic method that is noexcept if the other method is noexcept .

It is strange that this “other method” has two overloads, and the compiler chooses the second overload to determine magic noexcept -ness.

However, when magic is called later, the overload is called first , but noexcept -ness magic remains unchanged!

Here is the wandbox link

From what I understand:

  • noexcept(magic(dummy2{})) calls
  • noexcept(noexcept(adl_caller(...)) , which returns to
  • adl_caller(..., priority_tag<0>) noexcept , since user_method(dummy2) not known to the compiler at this point.

Fair, however, as user_method(dummy2) calls 3 lines above? Is this provided by the standard?

Sorry if I'm not clear enough.

 #include <iostream> template <unsigned N> struct priority_tag : priority_tag<N - 1> {}; template <> struct priority_tag<0> {}; template <typename T> auto adl_caller(T t, priority_tag<1>) noexcept(noexcept(user_method(t))) -> decltype(user_method(t)) { std::cout << "first adl_caller overload" << std::endl; user_method(t); } // tricky noexcept ... template <typename T> void adl_caller(T, priority_tag<0>) noexcept { std::cout << "second adl_caller overload" << std::endl; } template <typename T> void magic(T t) noexcept(noexcept(adl_caller(t, priority_tag<1>{}))) { adl_caller(t, priority_tag<1>{}); } struct dummy {}; struct dummy2 {}; // un-commenting this line makes the above call to cout print '0' // void user_method(dummy2); void user_method(dummy) { // user_method(dummy2) is declared after this point // this line prints '1', since magic falls back to the second adl_caller overload std::cout << "noexcept?: " << noexcept(magic(dummy2{})) << std::endl; std::cout << "dummy method called" << std::endl; // however, the first adl_caller overload is called here ... magic(dummy2{}); } void user_method(dummy2) { std::cout << "dummy2 method called" << std::endl; } int main() { magic(dummy{}); } 
+8
c ++ c ++ 11 noexcept templates argument-dependent-lookup
source share
1 answer

[temp.point] / 8 :

A specialization of a function template [...] can have several instantiation points inside a translation unit, and in addition to the instantiation points described above, for any such specialization that has an instantiation point within a translation unit, the end of a translation unit is also considered an instantiation point. [...] If two different points of instantiation give a typical specialization of different values ​​in accordance with one rule of definition, the program is poorly formed, no diagnostics are required.

Compare [temp.dep.candidate] :

To call a function where the postfix expression is a dependent name, candidate functions are found using the usual search rules ([basic.lookup.unqual], [basic.lookup.argdep]) except for this:

  • For the search part using the unqualified name search, only function declarations from the template definition context were found.

  • For the search part using the associated namespaces ([basic.lookup.argdep]), only function declarations found in the template definition context or template instance context.

If the call is poorly formed or finds a better match, search in the corresponding namespaces that are considered the entire function of the external link declaration, enter all translation units in these namespaces, and not only considering that these declarations are found in the context of the template definition and instance context template, then the program has undefined behavior.

+6
source share

All Articles