Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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

Tags:

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

like image 880
Nejc Avatar asked Jan 10 '19 10:01

Nejc


2 Answers

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.

like image 151
YSC Avatar answered Sep 20 '22 09:09

YSC


Along with @YSC's answer, also relevant is [temp.inst]/10:

An implementation shall not implicitly instantiate a function template, a variable template, a member template, a non-virtual member function, a member class, a static data member of a class template, or a substatement of a constexpr if statement , unless such instantiation is required.

like image 44
P.W Avatar answered Sep 24 '22 09:09

P.W