2016-09-13 10 views
9

Mam funkcję, która pobiera argumenty określonego typu szablonu; uproszczona wersja może wyglądać następująco:Czy istnieje sposób kanoniczny pozwalający na niejawną konwersję niestanowiącego stałego typu szablonu na stały?

#include <type_traits> 

template <typename T> 
struct foo 
{ 
    // default constructor 
    foo() { } 

    // simple copy constructor that can construct a foo<T> from a foo<T> 
    // or foo<const T> 
    foo(const foo<typename std::remove_const<T>::type> &) { } 
}; 

Funkcjonalnie foo zachowuje się podobnie do shared_ptr<T> z innej funkcjonalności rozszerzenie, które nie odnoszą się do tej kwestii. Semantyka funkcji dyktuje, że woli przyjąć foo<const T>. foo<const T> jest niejawnie constructible z foo<T>, więc chciałbym, aby móc zrobić coś jak następuje:

template <typename T> 
void bar(foo<const T> f) { } 

int main() 
{ 
    bar(foo<const int>()); // fine 
    bar(foo<int>());  // compile error 
} 

To nie działa, ponieważ nie istnieją żadne przeciążenia pasujące do bar że wziąć foo<int> (chociaż foo<const int> może być niejawnie wykonana z foo<int> rozdzielczość przeciążenie w porozumieniu z szablonu instancji wydaje się być bardziej restrykcyjne niż to.

Czy istnieje kanoniczny sposób do osiągnięcia tego celu? wiem, że mogę przedstawić drugą przeciążenie dla bar() że bierze foo<T> i wywołuje ręcznie do bar(foo<const T>), ale chciałbym uniknąć duplikowania, jeśli to możliwe.

+0

działa dobrze dla mnie, jak pokazano w moim pre-kompilator C++ 11 (bcc32) , ale nie na ideone.com za pomocą C++ 14 ("typy" const T 'i "int" mają niekompatybilne kwalifikatory cv). –

+0

Czy to tylko ja, czy zrobiłeś to na odwrót? Kopia c-tor używa 'remove_const', a twój opis i kod używający' bar' dotyczą przypadku, w którym ** dodaje ** 'const'. –

+0

@ YehezkelB .: Chodzi o to, że 'foo ' powinno być możliwe do skonstruowania z 'foo ', więc musisz usunąć 'const' w argumencie do konstruktora. Myślę, że * jest poprawny, jak napisano. –

Odpowiedz

5

Powodem kod nie działa, ponieważ jest niejawna konwersja jest zastosowanie po odliczeniu szablon argumentów. W takim przypadku foo<int> rzeczywiście nie będzie zgodne z foo<const T>, a kompilator nie będzie w stanie wywnioskować, co to jest T.Można próbować samemu sprawdzić określić typ bezpośrednio:

int main() 
{ 
    bar(foo<const int>()); // fine 
    bar<int>(foo<int>()); // also fine 
} 

Co można zrobić, to niech kompilator wziąć dowolny typ:

template <typename T> // T might be foo<int> 
void bar(T f) { } 

Lub jeśli chcesz, możesz pozwolić wydedukować kompilatora wewnętrzny T bez const:

template <typename T> // can deduce T as const int 
void bar(foo<T> f) { } 

Jeśli naprawdę chcą egzekwować constness (nawet w kodzie ogólne), warto dodać funkcję użytkową do swojej klasy, coś takiego:

foo<const T> as_const() const { return *this; } 

Więc podczas korzystania z funkcji rodzajowe, można wysłać jest const wersję swojej klasie:

bar<int>(foo<int>{}.as_const()); 
+0

Wynika to z kontekstu, w którym wywołuję funkcję szablonu, wygodnie jest jawnie określić typ argumentu szablonu, aby działał również dla mojej aplikacji. –

+0

Jeśli określisz typ argumentu szablonu, zamiast zaśmiecać swoje funkcje, możesz zdefiniować alias szablonu, aby pobrać to, z czego utworzono 'T' twoją klasę foo. –

6

Szablony nie pozwalają na konwersje!

Kiedy piszesz:

template <typename T> 
void bar(foo<const T> f) { } 

bar akceptuje foo<const T>, za każdym T. Nie akceptuje niczego innego. Nie ma znaczenia, że ​​foo<int> można zamienić na foo<const int>, że konwersja nigdy nie jest rozważana. Kropka.

Jeśli chcesz traktować odebrany f jak const można warunkowo uczynić go const:

// convert this version 
template <class T> foo<T const> make_const_f(foo<T> const& rhs) { return {rhs}; }  
// pass this version through 
template <class T> foo<T const>& make_const_f(foo<T const>& rhs) { return rhs; } 

template <typename T> 
void bar(foo<T> f) { 
    auto&& const_f = make_const_f(f); 

    // if T was const, const_f is a reference to f 
    // if T wasn't const, const_f is a new object of type foo<T const> 
} 
+0

Z propozycją 'as_const' dla' std', tworzenie darmowej funkcji z inną semantyką wydaje się złym pomysłem. – Yakk

+0

@Yakk Tak, nazywanie jest trudne. – Barry

+2

Programowanie wymaga dwóch trudnych zadań. Nazewnictwo, buforowanie i błędy "off-by-one-one". – Yakk

Powiązane problemy