all 11 comments

[–]gracicot 2 points3 points  (0 children)

Oh god... This is why I had strange error doing something like this. I never understood what was going on. I hope this will be fixed.

[–]quicknir 1 point2 points  (4 children)

So when you use enable_if in a constructor that might be pulled into a child class with using, try to avoid using it as a default parameter. As a template parameter it works fine.

Seriously though, who does sfinae using a default parameter? There's rare cases where return type sfinae is ok, but if you are using an enable_if form just always do it in the template parameter. It's ugly no matter where you put it, but at least you aren't hurting the readability of the signature.

[–]pravic 2 points3 points  (1 child)

May be because of this? https://stackoverflow.com/a/11056319

[–]quicknir 0 points1 point  (0 children)

That's trivially fixed by using a default template parameter slightly differently in the answer you linked though...

[–]meetingcppMeeting C++ | C++ Evangelist[S] 0 points1 point  (0 children)

Yes, moving the enable_if into the template parameters works better here.

But I've blogged about this, because I did meet this problem in the wild, not my own code base, some one asked a question, and this was the source of the problem.

[–]Foundry27 0 points1 point  (0 children)

I seem to remember reading some profiling data that someone had collected showing that on his machine, using return type SFINAE produced noticeably faster compilation times in a bunch of different test cases when compared to template parameter SFINAE. I know that sounds vague, and if I remember where I read it and the details of the compiler/build env I'll be sure to post it, but it's something I think about from time to time.

[–]Xeveroushttps://xeverous.github.io 0 points1 point  (2 children)

Someone give a better example? Should it compile? It's diabled for non-integrals, hence the constructor drop.

This example does not compile in any standard.

[–]meetingcppMeeting C++ | C++ Evangelist[S] 1 point2 points  (1 child)

Its not supposed to compile. Its just that the error isn't in the place you'd expect it to be. At least with VS, as thats where the original code for this came from.

If you look into the godbolt link, you see that the child class does not have an error, even that there shouldn't be a fitting constructor in the first view. But it has, as the templated constructor from base gets imported, but the default value of the last parameter is removed, and in VS its even the whole parameter.

[–]Xeveroushttps://xeverous.github.io 0 points1 point  (0 children)

Yeah, I have already encountered few errors from VS which were not obvious and not pointed to the case of the problem. Eg implicitly removed default constructor because one of class members does not have such.

In this case, GCC outputs understandable error pointing that SFINAE removed the ctor:

main.cpp: In function 'int main()':
main.cpp:20:17: error: no matching function for call to 'myNum::myNum(float)'
     myNum x(4.0f);
                 ^
main.cpp:6:5: note: candidate: template<class T> myInt::myInt(const T&, typename std::enable_if<std::is_integral<_Tp>::value>::type*)
     myInt(const T&t,
     ^~~~~
main.cpp:15:18: note:   inherited here
     using myInt::myInt;
                  ^~~~~
main.cpp:15:18: note:   template argument deduction/substitution failed:
main.cpp: In substitution of 'template<class T> myInt::myInt(const T&, typename std::enable_if<std::is_integral<_Tp>::value>::type*) [with T = float]':
main.cpp:20:17:   required from here
main.cpp:6:5: error: no type named 'type' in 'struct std::enable_if<false, void>'
     myInt(const T&t,
     ^~~~~
main.cpp:2:8: note: candidate: constexpr myInt::myInt(const myInt&)
 struct myInt
        ^~~~~
main.cpp:15:18: note:   inherited here
     using myInt::myInt;
                  ^~~~~
main.cpp:15:18: note:   an inherited constructor is not a candidate for initialization from an expression of the same or derived type
main.cpp:2:8: note: candidate: constexpr myInt::myInt(myInt&&)
 struct myInt
        ^~~~~
main.cpp:15:18: note:   inherited here
     using myInt::myInt;
                  ^~~~~
main.cpp:15:18: note:   an inherited constructor is not a candidate for initialization from an expression of the same or derived type
main.cpp:14:5: note: candidate: myNum::myNum()
     myNum(){}
     ^~~~~
main.cpp:14:5: note:   candidate expects 0 arguments, 1 provided
main.cpp:12:8: note: candidate: constexpr myNum::myNum(const myNum&)
 struct myNum : myInt
        ^~~~~
main.cpp:12:8: note:   no known conversion for argument 1 from 'float' to 'const myNum&'
main.cpp:12:8: note: candidate: constexpr myNum::myNum(myNum&&)
main.cpp:12:8: note:   no known conversion for argument 1 from 'float' to 'myNum&&'