Parametry szablonu innego niż typ

93

Rozumiem, że parametr szablonu innego niż typ powinien być stałym wyrażeniem integralnym. Czy ktoś może rzucić światło, dlaczego tak jest?

template <std::string temp>
void foo()
{
     // ...
}
error C2993: 'std::string' : illegal type for non-type template parameter 'temp'.

Rozumiem, czym jest stała integralna ekspresja. Jakie są powody, dla których nie zezwalasz na typy niestałe, takie std::stringjak w powyższym fragmencie?

Mahesh
źródło
17
Parametr szablonu jest rozpoznawany w czasie kompilacji.
Etienne de Martel

Odpowiedzi:

121

Nie możesz tego zrobić, ponieważ wyrażenia inne niż stałe nie mogą być analizowane i podstawiane w czasie kompilacji. Mogą się zmieniać w czasie wykonywania, co wymagałoby wygenerowania nowego szablonu w czasie wykonywania, co nie jest możliwe, ponieważ szablony są koncepcją czasu kompilacji.

Oto, na co pozwala standard dla parametrów szablonu innego niż typ (14.1 [temp.param] p4):

Parametr szablonu inny niż typ musi mieć jeden z następujących (opcjonalnie kwalifikowanych jako CV) typów:

  • typ całkowy lub wyliczeniowy,
  • wskaźnik do obiektu lub wskaźnik do funkcji,
  • l-wartość odniesienie do obiektu lub l-wartość odniesienie do funkcji,
  • wskaźnik do członka,
  • std::nullptr_t.
Xeo
źródło
6
@ALOToverflow: To znajduje się pod „wskaźnikiem do członka”. Jest to „wskaźnik do funkcji składowej” lub „wskaźnik do danych składowych”.
Xeo,
4
Warto zauważyć, że w przypadku wskaźników do obiektów (lub pól instancji), obiekty muszą mieć statyczny czas trwania i połączenie (zewnętrzne przed C ++ 11, wewnętrzne lub zewnętrzne w C ++ 11), aby wskaźniki do nich mogły być tworzone w czasie kompilacji.
Theodore Murdock
2
W C ++ 20 jest to teraz dozwolone pod warunkiem, że typ ma silną strukturalną równość, jest literałem, nie ma zmiennych / zmiennych podobiektów i gdzie operator statku kosmicznego jest publiczny.
Rakete1111
73

To nie jest dozwolone.

Jest to jednak dozwolone:

template <std::string * temp> //pointer to object
void f();

template <std::string & temp> //reference to object
void g();

Zobacz §14.1 / 6,7,8 w C ++ Standard (2003).


Ilustracja:

template <std::string * temp> //pointer to object
void f()
{
   cout << *temp << endl;
}

template <std::string & temp> //reference to object
void g()
{
     cout << temp << endl;
     temp += "...appended some string";
}

std::string s; //must not be local as it must have external linkage!

int main() {
        s = "can assign values locally";
        f<&s>();
        g<s>();
        cout << s << endl;
        return 0;
}

Wynik:

can assign values locally
can assign values locally
can assign values locally...appended some string
Nawaz
źródło
7
@Mahesh: Ponieważ szablon zasadniczo dotyczy szablonów, to adres tego std::stringwskaźnika lub obiektu referencyjnego. Gdyby ta zmienna była lokalna, prawdopodobnie otrzymywałbyś różne adresy za każdym razem, gdy funkcja była wywoływana.
Xeo,
11
@Mahesh: Nie wiesz, jak wygląda stos wywołań w czasie kompilacji. Przed twoją funkcją mogło być nazwanych 10 innych lub 3 inne lub jakkolwiek wiele innych, więc adres na stosie, pod którym tworzony jest łańcuch, może się zmieniać w zależności od wywołania. Kiedy masz obiekt z łączeniem zewnętrznym, jego adres jest ustalany podczas kompilacji / łączenia.
Xeo,
2
@Xeo „ jego adres jest ustalany podczas kompilacji / łączenia. Lub nie, dla kodu relokowalnego lub niezależnego od pozycji.
curiousguy
1
Ta odpowiedź (obecnie) nie wydaje się odpowiadać na pytanie PO, który pytał, dlaczego takie zachowanie istnieje; ta odpowiedź ogranicza się do powtórzenia przykładu PO bez podania jakiegokolwiek wyjaśnienia.
Quuxplusone
1
Spóźniłem się na imprezę, wygląda na to, że inteligentne wskazówki też nie działają
Nicholas Humphrey
28

Musisz umieć zmieniać argumenty szablonu

template <std::string temp>
void f() {
 // ...
}

f<"foo">();
f<"bar">(); // different function!?

Teraz impl musiałby wymyślić unikalną sekwencję znaków dla a std::string lub, w tym przypadku, dowolnej innej dowolnej klasy zdefiniowanej przez użytkownika, przechowującej określoną wartość, której znaczenie nie jest znane implementacji. Ponadto wartości dowolnych obiektów klas nie można obliczyć w czasie kompilacji.

Planuje się rozważyć dopuszczenie typów klas literałów jako typów parametrów szablonu dla post-C ++ 0x, które są inicjowane przez stałe wyrażenia. Mogłyby one zostać zniekształcone przez rekurencyjne zniekształcenie składowych danych zgodnie z ich wartościami (na przykład dla klas bazowych możemy zastosować przemierzanie w głąb, od lewej do prawej). Ale to na pewno nie zadziała dla dowolnych zajęć.

Johannes Schaub - litb
źródło
10

Argument szablonu inny niż typ podany na liście argumentów szablonu jest wyrażeniem, którego wartość można określić w czasie kompilacji. Takie argumenty muszą być:

wyrażenia stałe, adresy funkcji lub obiektów z łączami zewnętrznymi lub adresy statycznych członków klas.

Ponadto, literały łańcuchowe są obiektami połączonymi wewnętrznie, więc nie można ich używać jako argumentów szablonu. Nie możesz też użyć wskaźnika globalnego. Literały zmiennoprzecinkowe są niedozwolone, biorąc pod uwagę oczywistą możliwość błędów zaokrągleń.

Sadique
źródło
Użyłeś cytatu, więc jakie jest jego źródło? Chciałbym zagłosować za nim, jeśli ma źródło dla cytatu.
Gabriel Staples