5

Consider the following code. If my understanding of if constexpr is correct, the else branch should not be compiled so the z() should not be considered an error.

#include <type_traits>

struct Z{};

template<typename T>
void f(T z) {
  auto lam = [z]() {
    if constexpr(std::is_same<T, Z>::value) {
    } else {
      z();
    }
  };
}

int main() {
  f(Z{});
}

In clang and gcc this compiles; but with latest MSVC it does not. Unfortunately goldbolt's MSVC is too old, but on my machine with fully updated VS 2017, cl /std:c++17 yields:

Microsoft (R) C/C++ Optimizing Compiler Version 19.14.26428.1 for x86
Copyright (C) Microsoft Corporation.  All rights reserved.

if_constexpr.cpp
if_constexpr.cpp(10): error C2064: term does not evaluate to a function taking 0 arguments
if_constexpr.cpp(16): note: see reference to function template instantiation 'void f<Z>(T)' being compiled
        with
        [
            T=Z
        ]

If the enclosing lambda is removed, the code compiles on all three compilers.

Am I doing something wrong or unsupported, or is just a MSVC bug?

jcai
  • 3,448
  • 3
  • 21
  • 36
  • 3
    MSVC is known to very poorly support C++ features. It's been like this for years, it's never gonna change. You will probably need to define an overload `void f(Z z);` as a workaround. – bipll May 16 '18 at 13:27
  • 9
    If gcc and clang agree while MSVC disagrees, then there is a 99% chance that it's a bug in MSVC :) – Rakete1111 May 16 '18 at 13:27
  • @bipll Actually, in recent months it became a lot more standards compliant :) – Rakete1111 May 16 '18 at 13:27
  • What version of MSVS are you using? if constexpr was just recently added and was buggy to start with – NathanOliver May 16 '18 at 13:28
  • 1
    I'm not going to bother analysing this because it's Wednesday afternoon but I just want to say those guys above are _probably_ right. This is, after all, the entire point of `if constexpr`. – Lightness Races in Orbit May 16 '18 at 13:28
  • @Rakete1111 has Achilles approached the Tortoise? :-O – bipll May 16 '18 at 13:28
  • @bipll, Part of the reason for that was having an implementation from decades ago that even made it essentially impossible to support some features. The implementation has since been rejuvinated. GCC and Clang have had their share of bugs in newly-supported features. – chris May 16 '18 at 13:31
  • @chris ah, yeah, iteration N+1. – bipll May 16 '18 at 13:32
  • @NathanOliver MSVC 15.7.1, CL 19.14.26428.1. – jcai May 16 '18 at 13:33
  • @bipll - You might have missed Achilles' claim of capturing the turtle, as it happened just about a week ago. [Announcing: MSVC Conforms to the C++ Standard](https://blogs.msdn.microsoft.com/vcblog/2018/05/07/announcing-msvc-conforms-to-the-c-standard/) (Apparently some bugs remaining though. :-) – Bo Persson May 16 '18 at 13:39
  • @BoPersson Was it even ever unconformant?!! :-O – bipll May 16 '18 at 13:41
  • @bipll: Absolutely, nonconformant in some pretty significant ways, primarily for reasons of backward compatibility with C++ code written before there was a C++ Standard. Still is backward compatible rather than conformant, depending on which command-line options you pass. – Ben Voigt May 16 '18 at 14:58

1 Answers1

4

This is an MSVC bug. Please file a bug report.

The rule, from [stmt.if]/2, is:

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

During the instantiation of f<Z>, when we instantiate the condition we get true. That is not value-dependent, so the discard substatement (the one where we do z()) is not instantiated. It's only the instantiation of z() that leads to the error - and it should not be happening.

Barry
  • 286,269
  • 29
  • 621
  • 977