I am looking into class template deduction available since C++17. Here are the code I would like to ask about:
#include <iostream>
#include <cmath>
using std::endl;
using std::cout;
template<typename T>
struct MyAbs {
template<typename U>
MyAbs(U&& u) : t(std::forward<T>(u))
{ cout << "template" << endl;}
#ifdef ON
MyAbs(const T& t) : t(t) {}
#endif
T operator()() const
{
return std::abs(t);
}
T t;
};
/*
// may need the following
template<typename U>
MyAbs(U&&) -> MyAbs<typename std::remove_reference<U>::type>;
*/
int main()
{
const double d = 3.14;
cout << MyAbs(4.7)() << endl;
cout << MyAbs(d)() << endl;
return 0;
}
When MyAbs(const T&)
is not conditionally-compiled (i.e. no -DON
), both clang++ and g++ fail to deduce the template parameter, T
. Given -DON=1
, both compilers build the simple example above.
Firstly, I also guessed that the deduction should fail; the compiler could deduce U
but not T
. The compile errors I got were what I expected. Please, let me know if I am mistaken.
If I was correct on it, then, I cannot understand why the deduction with U&&
succeeds when MyAbs(const T&)
is added. What I expected was deducing with U&&
fails, and SFINAE allows me to invoke MyAbs(const T&)
instead for both cases: 4.7 and d. However, what happened is different. This program seems to invoke the template version for 4.7 and non-template version for d
.
$ g++ -Wall -std=c++17 ~/a.cc -DON=1
$ ./a.out
template
4.7
3.14
It seems that the template version has suddenly become viable. Is this expected? If so, what's the reason?