Непоследовательное поведение между компиляторами в отношении создания шаблона в отброшенном операторе 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, если только такая реализация не реализована необходимо.