Inconsistent behaviour across compilers in regard to instantiation of a template in a discarded if constexpr(false) statement

  • A+
Category:Languages

I am trying to understand whether the snippet below should compile according to The Standard or not. When I try to compile it with latest version of three major compilers, the following occurs:

  • Clang (version 7.0.0, with -std=c++17 flag): compiles fine;
  • GCC (version 8.2, with -std=c++17 flag): also compiles fine;
  • MSVC (version 19.16, with /std:c++17 flag): compiler error (see below).

The error occurs because the MSVC compiler seemingly tries to instantiate std::optional<void> despite the fact that the code is discarded. GCC and Clang don't seem to do that.

Does The Standard clearly define what should occur in this case?

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

Error by 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   ] 

Live demo

 


Definitively a bug of MSVC. A bug report exist and has been reportedly fixed in Visual Studio 2019 Preview.


if constexpr is standardized in [stmt.if]/2:

If the if statement is of the form if constexpr, the value of the condition shall be a contextually converted constant expression of type bool; this form is called a constexpr if statement.

This applies.

If the value of the converted condition is false, the first substatement is a discarded statement, otherwise [...].

It also applies, making in your program { std::optional<T> val; } a discarded statement.

During the instantiation of an enclosing templated entity (ndYSC Bar<void, int>), if the condition is not value-dependent after its instantiation, the discarded substatement (if any) is not instantiated.

Comment

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen: