条件noexcept和重载的不一致

Dan*_*nte 9 c++ templates noexcept argument-dependent-lookup c++11

我有一个与问题非常相似的问题.

简而言之,我有一个magic方法,noexcept如果是另一种方法noexcept.

奇怪的是,这个"另一个方法"有两个重载,编译器选择第二个重载来确定magic noexcept-ness.

但是,当magic稍后调用时,会调用第一个重载,但是noexcept-ness 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{});
}
Run Code Online (Sandbox Code Playgroud)

T.C*_*.C. 6

[temp.point]/8:

函数模板的特化可以在翻译单元中具有多个实例化点,并且除了上述实例化点之外,对于在翻译单元内具有实例化点的任何这样的专业化,结束翻译单位也被认为是实例化的一个方面.[...]如果两个不同的实例化点根据一个定义规则给出模板特化的不同含义,则程序形成错误,无需诊断.

比较[temp.dep.candidate]:

对于postfix-expression是从属名称的函数调用,使用通常的查找规则([basic.lookup.unqual],[basic.lookup.argdep])找到候选函数,除了:

  • 对于使用非限定名称查找的查找部分,仅找到模板定义上下文中的函数声明.

  • 对于使用关联命名空间([basic.lookup.argdep])查找的部分,仅找到在模板定义上下文或模板实例化上下文中找到的函数声明.

如果调用结果不正确或者找到更好的匹配,那么相关命名空间中的查找会考虑在所有翻译单元中的那些名称空间中引入外部链接的所有函数声明,而不仅仅是考虑模板定义和模板中的那些声明实例化上下文,然后程序有未定义的行为.