2

This code works:

// g++ -std=c++11
// (or)
// clang++ -std=c++11

#include <iostream>

template <class T>
struct Tester
{
    template <class S = T, class = decltype(S())>
    static void Test (int && k)
    {
        std::cout << "Default constructible" << std::endl;
    }

    static void Test (...)
    {
        std::cout << "Not default constructible" << std::endl;
    }
};

struct HasDefaultConstructor
{
    HasDefaultConstructor() = default;
};

struct NoDefaultConstructor
{
    NoDefaultConstructor() = delete;
};

int main ()
{   
    Tester<HasDefaultConstructor>::Test(int());
    Tester<NoDefaultConstructor>::Test(int());

    return 0;
}

But I'd like to understand why I need to set up S for the indirect template deduction template <class S = T, class = decltype(S())>.

I'd prefer to do it more simply by using template <class = decltype(T())>, but in gcc this results in wrong outputs: all calls to the Test method go to the first one; and in clang this results in an error call to deleted constructor of 'NoDefaultConstructor' (Thanks to HolyBlackCat for pointing out the clang compile error here).

Why? What is the process of the compiler / c++ standard that forces us to indirectly refer to the class templates?

Elliott
  • 2,603
  • 2
  • 18
  • 35
  • Current GCC seems to do what you want here (whether that’s *correct* is a separate question). – Davis Herring Dec 21 '19 at 18:27
  • @DavisHerring What flags are you using? Mine complies, but incorrectly says that both of the example types have default constructors (I mention this in the question). – Elliott Dec 21 '19 at 18:51
  • Nothing interesting: just [`-std=c++11 -pedantic-errors`](https://wandbox.org/permlink/djRmVnVq8D7I6vYO). – Davis Herring Dec 21 '19 at 20:16

1 Answers1

2

This is because you first instantiate the template class Tester. So the requirement class = decltype(T()) becomes a requirement on the template class Tester<T> itself. So if it isn't satisfied the template class is malformed. Edit: imagine ypu actually wanted to write a template function with X as template parameter and uses class X = decltype(T()) as default entry? How compiler would tell it apart from SFINAE stuff?

The indirect deduction template <class S = T, class = decltype(S())> transforms it into requirement on the template function Test instead of the whole class.

This is the technical reason. Yeah, nobody likes SFINEA and function meta programming which is why concepts are being introduced in C++20 and constexpr programming is being developed.

ALX23z
  • 4,456
  • 1
  • 11
  • 18
  • Your edit is a really good explanation as to why, in both examples, the compiler can’t know that I’d like to SFINAE one of the methods away. Thanks. – Elliott Dec 21 '19 at 18:54
  • 1
    As to why GCC allows `decltype(T())` as a template argument when `T()` is invalid, it's probably a bug. There's a similar bug [#57943](https://gcc.gnu.org/bugzilla//show_bug.cgi?id=57943) for this. Also see [this q](https://stackoverflow.com/questions/6972368/stdenable-if-to-conditionally-compile-a-member-function). – rustyx Dec 21 '19 at 19:05