Prevent Implicit Template Creation

In such a situation, overloading a method like this:

struct A { void foo( int i ) { /*...*/ } template<typename T> void foo( T t ) { /*...*/ } } 

How can I prevent a template from being instantiated unless explicitly stated ?:

 A a; a.foo<int>( 1 ); // ok a.foo<double>( 1.0 ); // ok a.foo( 1 ); // calls non-templated method a.foo( 1.0 ); // error 

Thanks!

+6
source share
3 answers

You can enter a depedent_type structure that prevents the output of a template argument .

 template <typename T> struct dependent_type { using type = T; }; struct A { void foo( int i ) { /*...*/ }; template<typename T> void foo( typename dependent_type<T>::type t ) { /*...*/ } } 

What in your example:

 a.foo<int>( 1 ); // calls the template a.foo<double>( 1.0 ); // calls the template a.foo( 1 ); // calls non-templated method a.foo( 1.0 ); // calls non-templated method (implicit conversion) 

wandbox example

(This behavior is explained in cppreference > template argument output > inference contexts .)


If you want to make a.foo( 1.0 ) compilation error, you need to limit the first overload:

 template <typename T> auto foo( T ) -> std::enable_if_t<std::is_same<T, int>{}> { } 

This method does the aforementioned overload of foo to accept only int arguments: implicit conversions (e.g. float to int ) are not allowed. If this is not what you want, consider TemplateRex answer.

wandbox example

(With the limited function above, there is a curious interaction between the two overloads when calling a.foo<int>( 1 ) . I asked a question about this because I am not sure about the underlying rules that lead to it.)

+9
source

By far, the easiest way to do what you want is to explicitly remove the overload you don't want:

 void foo(double) = delete; 

those. to get the following explicit example:

 #include <iostream> struct A { void foo(int) { std::cout << __PRETTY_FUNCTION__ << "\n"; } void foo(double) = delete; template<typename T> void foo( T ) {std::cout << __PRETTY_FUNCTION__ << "\n"; } }; int main() { A a; a.foo<int>( 1 ); // ok a.foo<double>( 1.0 ); // ok a.foo( 1 ); // calls non-templated method a.foo( 1.0 ); // error } 

With the last line in the main note, prints

 void A::foo(T) [with T = int] void A::foo(T) [with T = double] void A::foo(int) 

and with the last line remaining in this print

 prog.cc: In function 'int main()': prog.cc:18:16: error: use of deleted function 'void A::foo(double)' a.foo( 1.0 ); // error ^ prog.cc:6:10: note: declared here void foo(double) = delete; 
+1
source

Adding another offer to the bank, and in the same spirit as Vittorio's answer, you can add another template parameter to the signature:

 template <class UserType, class InputType> void foo(InputType x){...} 

Then, to use it, you must specify the first parameter of the template, since it cannot be output. This has a slight advantage in distinguishing between what the user wanted and what was transferred, which may be useful in some cases.

0
source

Source: https://habr.com/ru/post/1014054/


All Articles