Why do C ++ patterns match if the method does not check the type?

The following code does not compile because struct A does not support the -- operator.

 struct A {}; struct B { void Run() {} A& Dec(A& a) { return --a; } }; int main(int argc, char** argv) { B b; b.Run(); } 

Same thing with this code.

 struct A {}; template <class T> struct B { void Run() {} A& Dec(A& a) { return --a; } }; int main(int argc, char** argv) { B<A> b; b.Run(); } 

So why this compilation (in C ++ 11)?

 struct A {}; template <class T> struct B { void Run() {} T& Dec(T& a) { return --a; } }; int main(int argc, char** argv) { B<A> b; b.Run(); } 

It seems that instantiating the template does not automatically create instances of unused methods inside the template, which depend on the type parameter for type checking, which means that the template will match even if some of its methods do not. This is disappointing because I was hoping to use SFINAE to determine the applicability of various methods and operators to types, but if the template replacement succeeds even when method calls fail at compile time, this method will not work.

+8
c ++ c ++ 11 templates sfinae
source share
1 answer

It was decided (by the C ++ committee) that the methods of template classes will have their own instances only if they are used.

This makes it easy to write C ++ code due to hard errors when they are used.

As an example, std::vector uses it with std::vector::operator< ; if you have no call < , this is a mistake. If you do, the call will work.

More modern C ++ will encourage SFINAE to disable it, so you can make sure that < is safe or not, but this method was not used when std::vector was developed. You can see the evolution of this technique in std::function , which came from hungry for almost anything in the universal constructor, so that this constructor was only considered to allow overloading when it worked between C ++ 11 and C ++ 14.

If you want SFINAE, you cannot rely on code bodies like this. To facilitate the loading of compilers, compilers should check for function definition declarations when running SFINAE tests.

Part of the reason is that SFINAE on expressions is complex; on whole bodies harder. The compiler must carefully compile the body of the function, click on the error, and then return to the "nope, nothing done" state.

Errors in the body of functions are always difficult errors. You cannot avoid this in the current version of C ++.

Now you can write functions that decide whether or not there will be errors, but there really is no error, and then use their bodies to determine if another code will be an error. For example:

 template<class T> auto foo() { constexpr if(sizeof(T)<4) { return std::true_type{}; } else { return std::false_type{}; } 

you can use foo<char>() in some SFINAE somewhere, and its true or false -ness can make another overload replacement or not.

Note that the error (if any) still occurs outside the function body ( foo here).

+5
source share

All Articles