2017-01-13 54 views
6

W sytuacji przeciążenia metody takie jak to:Prevent niejawna szablon instancji

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

Jak mogę zapobiec szablonu instancji, chyba że wyraźnie nakazał ?:

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 

Dzięki!

+3

Czy jesteś pewien, że to konieczne? Dlaczego nie po prostu specjalizować szablon 'int' zamiast przeciążać funkcję? –

+0

@ChristianHackl po prostu wykonaj 'foo (double) = delete', zobacz moją odpowiedź. – TemplateRex

Odpowiedz

9

Można wprowadzić strukturę depedent_type, która zapobiega template argument deduction.

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) { /*...*/ } 
} 

Które w Twojej przykładzie: (. To zachowanie jest wyjaśnione na cppreference >template argument deduction >non-deduced contexts)

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


Jeśli chcesz, aby a.foo(1.0) C Błąd ompilation, trzeba ograniczyć pierwszy przeciążeniem:

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

Technika ta sprawia, że ​​powyżej przeciążenie foo podjąć tylko int argumenty: niejawne konwersje (np float do int) są niedozwolone. Jeśli nie jest to to, czego chcesz, rozważ odpowiedź TemplateRex.

wandbox example

(Z powyższej ograniczonej funkcji, jest ciekawy interakcji pomiędzy dwoma przeciążeń podczas a.foo<int>(1) nazywa. I asked a question about it jak nie jestem pewien zasad leżących u ich podstaw, które je prowadzą.)

+0

może być znacznie prostsze, z 'foo (double) = delete', bez potrzeby dla enable_if i nie wydedukowanego wrappera, zobacz mój anwer :) – TemplateRex

+0

To wydaje mi się najlepszą opcją dla mnie. Innym sposobem na uniknięcie 'a.foo (1.0)' przy mniejszych wymaganiach zgodności kompilatora jest zrobienie czegoś takiego jak 'static_assert (std :: is_same ," ... ");'. – cyberguijarro

1

zdecydowanie najprostszym sposobem, aby robić to, co chcesz, jest jawnie usunąć przeciążenie nie chcesz:

void foo(double) = delete; 

Iemieć następującą wyraźny przykład:

#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  
} 

Z ostatniej linii w głównym zakomentowanym this prints

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

i ostatniej linii w lewo w this prints

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; 
+0

Należy zauważyć, że to podejście pozwala na wywołanie 'foo' z' float', 'char' * (i wszystkimi typami niejawnie wymienialnymi na' int') *. Nie jestem pewien, czy tego właśnie chce OP. –

+0

@VittorioRomeo z pewnością, ale to samo dotyczy twojego rozwiązania: czy chce dokładnie "int", czy tylko niepływające, czy tylko nie zwężające się konwersje, itp. W każdym przypadku, rozwiązanie '= delete' może zostać rozszerzone do wyklucz (ale nie usuń, ponieważ chcesz błędu) inne przeciążenia. – TemplateRex

+0

Tak, nie krytykuję twojej odpowiedzi - po prostu mówiąc, że prawdopodobnie powinieneś o tym wspomnieć. Zaktualizuję odpowiedź, aby jasno stwierdzić, że niejawne konwersje nie są akceptowane. –

0

Dodanie kolejnego sugestię do garnka, w podobny sposób jak odpowiedź Vittorio można również dodać inny parametr szablonu do podpisu:

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

Następnie do wykorzystania go należy określić pierwszy parametr szablonu, ponieważ nie może być wywnioskować. Ma to niewielką zaletę polegającą na możliwości rozróżnienia między tym, czego użytkownik chciał, a tym, co zostało przekazane, co może być przydatne w niektórych przypadkach.