6

I learnt about the SFINAE principle and its various uses. Then I wrote the following program that compiles with gcc but not with msvc and clang. Live demo.

#include <iostream>
#include <type_traits>

template <typename T> class Container {
    public:

        template<typename U = T> 
        std::enable_if_t<std::is_same_v<T, int>> foo(const T&)
        {

        }
};

template<typename T>
void func(T&& callable)
{
    Container<int> c;
    (c.*callable)(4);
}
int main(){

    //works with gcc but not with clang and msvc
    func(&Container<int>::foo); 
} 

As we can see the above program works with gcc but not with clang and msvc and I don't know which compiler is right here. So is this program well-formed or ill-formed etc.

Alan
  • 116
  • 9
  • 1
    The problem isn't about `enable_if`. Change it to `void` and you should get the same errors. – Ranoiaetep Nov 25 '22 at 13:20
  • Cool. Works ok if explicitly specialize `foo` at the point where a pointer to the `foo` is taken. Also, it's either member functions are automatically "skipped" when not used so you don't need to disable them, or there are more than one such function and all-but-one are disabled for each "taking of a pointer", but then you still have to disambiguate somehow which function you're interested in when taking a pointer to it. Do you have a use-case? – smitsyn Nov 25 '22 at 13:23
  • 2
    The program is well-formed due to [CWG 2608](https://cplusplus.github.io/CWG/issues/2608.html). – Jason Nov 25 '22 at 13:24
  • 4
    BTW, your `enable_if` usage is wrong: instantiating `Container` would produce hard error, you need `std::enable_if_t>`. In C++20, a `requires(std::is_same_v)` (and remove the template) would simplify things. – Jarod42 Nov 25 '22 at 13:54
  • What compile error(s) did you receive from clang and msvc? – Robert Columbia Dec 08 '22 at 10:28

1 Answers1

9

This is CWG 2608 and the program is well-formed.

If all of the template arguments can be deduced or obtained from default template-arguments, they may all be omitted; in this case, the empty template argument list <> itself may also be omitted.

(emphasis mine)

Note the added part which makes func(&Container<int>::foo); well-formed as the template argument for U can be obtained from the default template argument and hence it may be omitted which implies that the empty template argument list <> itself may also be omitted. Thus, gcc is standard conformant.

Here is the clang bug:

Clang rejects valid program with default argument

Here is the msvc bug:

MSVC rejects valid program with default argument

Jason
  • 36,170
  • 5
  • 26
  • 60