Несоответствия с условным noexcept и перегрузками

у меня есть проблема, которая очень похожа на этой один.

короче говоря, у меня есть magic метод, который noexcept если другой метод noexcept.

странно то, что этот "другой метод" имеет две перегрузки, и компилятор выбирает второй перегрузка для определения magic noexcept-Несс.

, когда magic вызывается позже,первый вызывается перегрузка, но noexceptмыс magic остается той же!

вот wandbox ссылке

из того, что я понимаю:

  1. noexcept(magic(dummy2{})) звонки
  2. noexcept(noexcept(adl_caller(...)), который возвращается к
  3. adl_caller(..., priority_tag<0>) noexceptС user_method(dummy2) не известен компилятору на данный момент.

справедливо, однако, как user_method(dummy2) называется 3 строки выше? Это предусмотрено стандартом?

Извините, если я не ясно достаточно.

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

1 ответов


[temp.точки]/8:

специализация шаблона функции [...] мочь иметь несколько точек экземпляров в единице перевода, и в дополнение к пунктам создания экземпляра, описанным выше, для любого такая специализация, которая имеет точку инстанцирования внутри переводческая единица, конец переводческой единицы также считается точка создания экземпляра. [...] Если две разные точки инстанцирования дать шаблон специализация различные значения согласно правило одно-определения, программа плохо сформирована, никакой диагностики требуемый.

сравнить [temp.отдел.кандидат]:

для вызова функции, где постфиксное-выражение - это зависит от имя, функции-кандидаты найдены с использованием обычных правил поиска ([основной.уважать.unqual], [basic.уважать.argdep]) кроме того, что:

  • для части подстановка с помощью подстановки неквалифицированных имен найдены только объявления функций из контекста определения шаблона.

  • для части поиска с использованием связанных пространств имен ([basic.уважать.argdep]), только объявления функций в контекст определения шаблона или контекст создания экземпляра шаблона найдены.

Если вызов будет плохо сформирован или найдет лучшее совпадение, поиск в связанном пространства имен рассматривали все функции объявления с внешней связью, введенные в эти пространства имен в все переводческие единицы, а не только с учетом найденных деклараций затем в контекстах определения шаблона и создания экземпляра шаблона программа имеет неопределенное поведение.