jeudi 3 janvier 2019

Different behavior across compilers for std::enable_if (dependent on Outer class template parameters)

I'm have a nested (Inner) class, for which I want to enable_if a constructor, depending on how many template parameters (Args) the enclosing class (Outer) has.

I came up with the code below, only to find out that it compiles fine on some compilers, and on some not.

#include <tuple>
#include <type_traits>


template <typename... Args>
struct Outer {

    struct Inner {
        Inner(const Outer* out, Args... vals) 
            : outer(out)
            , values(vals...)
         {}

         // This ctor should be enabled only if Args are non-empty
         template <typename = std::enable_if_t<(sizeof...(Args) > 0)>>
         Inner(const Outer* out)
            : outer(out)
         {}

        const Outer* outer;
        std::tuple<Args...> values;
    };

};

int main()
{
    Outer<int, int> o1;
    Outer<int, int>::Inner i1(&o1, 1, 2);
    Outer<int, int>::Inner i11(&o1);

    Outer<> o2;
    Outer<>::Inner i2(&o2);
    Outer<>::Inner i21(nullptr);

}

Shown on Godbolt: https://godbolt.org/z/lsivO9

The funny part are the results:

  • GCC 8.2 -std=c++17 - FAIL to compile
  • GCC trunk -std=c++17 - OK
  • MSVC 19.14 /std:c++17 /permissive- - OK
  • MSVC 19.16 /std:c++17 /permissive- - OK
  • clang 7 -std=c++17 - FAIL to compile
  • clang trunk -std=c++17 - FAIL to compile

So, the questions:

  • Are the Args... of the Outer class in the immediate context of the Inner class?
  • Is the above example well-formed?
  • Which compiler is right?
  • Why GCC started to behave differently with trunk?

Aucun commentaire:

Enregistrer un commentaire