Непоследовательное поведение между компиляторами в отношении создания шаблона в отброшенном операторе constexpr (false)

Я пытаюсь понять, должен ли приведенный ниже фрагмент компилироваться в соответствии со стандартом или нет. Когда я пытаюсь скомпилировать его с последней версией трех основных компиляторов, происходит следующее:

  • Clang (версия 7.0.0, с -std=c++17): прекрасно компилируется;
  • GCC (версия 8.2, с -std=c++17): также хорошо компилируется;
  • MSVC (версия 19.16, с флагом /std:c++17): ошибка компилятора (см. Ниже).

Ошибка возникает из-за того, что компилятор MSVC пытается создать экземпляр std::optional<void> несмотря на то, что код отбрасывается. GCC и Clang, похоже, этого не делают.

Стандарт четко определяет, что должно произойти в этом случае?

#include <optional>  
#include <type_traits>
template<typename T, typename... Args>
struct Bar
{
  void foo(Args... args)
  {
    if constexpr(!std::is_same_v<T, void>) // false
    {
      // MSVC compiler error occurs because of the line below; no error occurs when compiling with GCC and Clang 
      std::optional<T> val; 
    }
  }
};
int main(int argc, char** argv)
{
  Bar<void, int> inst;
  inst.foo(1);
  return 0;
}

Ошибка MSVC:

C:/msvc/v19_16/include\optional(87): error C2182: '_Value': illegal use of type 'void'

C:/msvc/v19_16/include\optional(128): note: see reference to class template instantiation 'std::_Optional_destruct_base<_Ty,false>' being compiled
  with
  [
       _Ty=void
  ]

Живая демо

Ответ 1

Определенно ошибка MSVC. Отчет об ошибке существует и, как сообщается, был исправлен в Visual Studio 2019 Preview.


if constexpr стандартизирован в [stmt.if]/2:

Если оператор if имеет форму if constexpr, значение условия должно быть контекстно-преобразованным константным выражением типа bool; эта форма называется оператором constexpr.

Это относится.

Если значение преобразованного условия ложно, первое подстановка является отвергнутым оператором, в противном случае [...].

Это также применимо, делая в вашей программе { std::optional<T> val; } { std::optional<T> val; } отклоненное выражение.

Во время создания включающей шаблонной сущности (ndYSC Bar<void, int>), если условие не зависит от значения после его создания, отброшенное подзаголовок (если есть) не создается.

Ответ 2

Наряду с ответом @YSC также имеет значение [temp.inst]/10:

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