7

Here is an extremely simple code :

template <typename... Args,
 typename std::enable_if<std::less<int>()(sizeof...(Args), 3), int>::type* = nullptr>
void test(std::tuple<Args...>)
{

}

int main()
{
    test(std::make_tuple(1, 2));
}

It's just simple function template with some enable_if condition. (for further SFINAE).

But it fails to compile in Visual Studio 2019 with C++ 17 setup.

error C2672:  'test': no matching overloaded function found
error C2783:  'void test(std::tuple<_Types...>)': could not deduce template argument for '__formal'

However I found that it compiles well in GCC and Clang. Why does that seemingly innocent code fail?

funny thing is if I substitute sizeof...(Args) to 2 then it suddenly works.

Edit : My original question doesn't provide type in enable_if, but I found that void* is not allowed as a non-type template parameter in C++ 17. But It doesn't matter. Because even if I change to std::enable_if<std::less<int>()(sizeof...(Args), 3), int>, it still fails with same error.

i.stav
  • 444
  • 3
  • 9
  • 1
    Why not just `sizeof...(Args) < 3`, anyway? ;-) – L. F. Jul 04 '19 at 05:01
  • Note that `void*` non-type template parameters are not allowed in C++17. See https://stackoverflow.com/q/56003162 – L. F. Jul 04 '19 at 05:07
  • 1
    @L.F. wow, I didn't know that. But I think MSVC is still wrong (or at least inconsistent) because it works if I change ``sizeof`` to ``2``. – i.stav Jul 04 '19 at 05:11
  • Yep, that's nothing more than a coincidental correctness resulted from two mistakes. – L. F. Jul 04 '19 at 05:12
  • [It does work](https://godbolt.org/z/_7DXmd) if you change the template to an anonymous type instead of a non-type, interesting. – Timo Jul 04 '19 at 08:19

1 Answers1

3

Per [comparisons.less]:

template <class T = void> struct less {
  constexpr bool operator()(const T& x, const T& y) const;
};

constexpr bool operator()(const T& x, const T& y) const;

Returns: x < y.

The operator is constexpr. Therefore, your code is fine as far as the less is concerned.

However, technically speaking, MSVC is actually right here — non-type template parameters shall not have type void* in C++17. MSVC actually diagnosed that. That's purely a coincidence, anyway.

You can use < directly as a workaround:

template <typename... Args,
  typename std::enable_if<(sizeof...(Args) < 3), int>::type = 0>
void test(std::tuple<Args...>)

(Note that int is used instead of void*, so that a language pedant has absolutely nothing to say.)

L. F.
  • 19,445
  • 8
  • 48
  • 82
  • 2
    Thanks. I already knew how to solve this problem, but my question is rather "what makes this simple code fail in MSVC ??" – i.stav Jul 04 '19 at 05:09
  • 2
    @i.stav MSVC is known to fail mysteriously at times ;-) – L. F. Jul 04 '19 at 05:10
  • 1
    well, it still fails if I provide ``int`` to ``enable_if`` but still keeping ``std::less``. So MSVC is completely wrong – i.stav Jul 04 '19 at 05:16