Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Evaluating noexcept specifier before template type deduction

Please see the following code:

#include <utility>

struct A {
  A(int, int) {}
};

struct tag {};

template <class... Args>
struct is_noexcept {
  static constexpr bool value = noexcept(A{std::declval<Args>()...});
};

struct B : A {
  //#1
  template <class... Args>
  B(tag, Args&&... args) noexcept(/*Here*/is_noexcept<Args...>::value) :
    A{std::forward<Args>(args)...} {}

  //#2
  B(int x, int y) : A{x, y} {}
};

int main()
{
  B x{0, 0};
}

This code seems to be accepted by GCC/Clang, but MSVC 2017 rejects it. It seems that MSVC compiler tries to compute the noexcept specifier before understanding that #1 is not an appropriate overload (due to incompatibility between tag and int) so should be discarded. Thus it tries to evaluate is_noexcept<int>::value and finds out noexcept(A{std::declval<int>()}) is ill-formed. Since this happens not in the immediate context , this is not where SFINAE comes in, so hard error.

(In fact, I'm not pretty sure about this, but I've confirmed that if I put noexcept(A{std::declval<Args>()...}) instead of is_noexcept<Args...>::value at /*Here*/ to make the failure inside the immediate context, the MSVC compiler happily drops #1 and calls #2. Is this right?)

I suspect GCC/Clang is right and MSVC is wrong, but which one is correct?

like image 467
Junekey Jeon Avatar asked Jul 24 '18 14:07

Junekey Jeon


1 Answers

This is an MSVC bug. The rule, as a result of CWG 1330, in [except.spec]/13 is:

An exception specification is considered to be needed when:

  • in an expression, the function is the unique lookup result or the selected member of a set of overloaded functions ([basic.lookup], [over.match], [over.over]);
  • the function is odr-used or, if it appears in an unevaluated operand, would be odr-used if the expression were potentially-evaluated;
  • the exception specification is compared to that of another declaration (e.g., an explicit specialization or an overriding virtual function);
  • the function is defined; or
  • the exception specification is needed for a defaulted special member function that calls the function. [ Note: A defaulted declaration does not require the exception specification of a base member function to be evaluated until the implicit exception specification of the derived function is needed, but an explicit noexcept-specifier needs the implicit exception specification to compare against. — end note ]

The exception specification of a defaulted special member function is evaluated as described above only when needed; similarly, the noexcept-specifier of a specialization of a function template or member function of a class template is instantiated only when needed.

The exception specification of B(tag, Args&&...) is not needed (we don't satisfy any of those bullets), so it should not be instantiated.

Note also that substitution failure in the exception specification is not part of the immediate context, and would not be a SFINAE-friendly error.

like image 75
Barry Avatar answered Oct 27 '22 00:10

Barry