Нетипові параметри шаблону


93

Я розумію, що параметр нетипового шаблону повинен бути постійним інтегральним виразом. Хтось може пролити світло, чому це так?

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

Я розумію, що таке постійний інтегральний вираз. Які причини забороняють нестійкі типи, як std::stringу наведеному вище фрагменті?


17
Параметр шаблону вирішується під час компіляції.
Етьєн де Мартель

Відповіді:


121

Причина, по якій ви цього не можете зробити, полягає в тому, що несталі вирази не можуть бути проаналізовані та замінені під час компіляції. Вони можуть змінюватися під час виконання, що вимагатиме створення нового шаблону під час виконання, що неможливо, оскільки шаблони є поняттям часу компіляції.

Ось що стандарт дозволяє для нетипових параметрів шаблону (14.1 [temp.param] p4):

Нетиповий шаблон-параметр повинен мати один із наступних (необов'язково cv-кваліфікованих) типів:

  • цілісний або тип перерахування,
  • вказівник на об'єкт або вказівник на функцію,
  • посилання lvalue на об'єкт або посилання lvalue на функцію,
  • вказівник на член,
  • std::nullptr_t.

6
@ALOToverflow: Це потрапляє під "вказівник на учасника". Це або "вказівник на функцію члена", або "вказівник на дані члена".
Xeo

4
Варто зазначити, що у випадку вказівників на об'єкти (або поля екземпляра), об'єкти повинні мати статичну тривалість зберігання та зв'язок (зовнішні перед C ++ 11, внутрішні або зовнішні в C ++ 11), щоб вказівники на них могли бути створеним під час компіляції.
Теодор Мердок,

2
У C ++ 20 це тепер дозволено за умови, що тип має чітку структуровану рівність, є літералом, не має змінних / мінливих підоб'єктів і де оператор космічного корабля є загальнодоступним.
Rakete1111

73

Це заборонено.

Однак це дозволено:

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

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

Див. § 14.1 / 6,7,8 у C ++ Standard (2003).


Ілюстрація:

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

Вихід:

can assign values locally
can assign values locally
can assign values locally...appended some string

7
@Mahesh: Оскільки те, що шаблон в основному шаблони, це адреса цього std::stringвказівника або об'єкта посилання. Якби ця змінна була локальною, ви могли б отримувати різні адреси кожного разу, коли була викликана функція.
Xeo,

11
@Mahesh: Ви не знаєте, як виглядає стек викликів під час компіляції. До вашої функції можна було викликати 10 інших або 3 інших або як би багато інших, тому адреса в стеку, за якою створюється рядок, може змінюватися від виклику до виклику. Коли у вас є об’єкт із зовнішнім зв’язком, його адреса фіксується під час компіляції / зв’язку.
Xeo,

2
@Xeo " його адреса фіксується під час компіляції / зв'язування. " Або ні, для переміщуваного коду або коду, що не залежить від позиції.
curiousguy

1
Ця відповідь (на даний момент), схоже, не стосується питання ОП, яке запитувало, чому така поведінка існує; ця відповідь просто повторює приклад OP без жодних пояснень.
Quuxplusone

1
Я запізнююсь на вечірку, здається, розумні покажчики теж не працюють
Ніколас Хамфрі

28

Вам потрібно вміти керувати аргументами шаблону

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

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

Тепер імпл повинен буде створити унікальну послідовність символів для, std::stringабо, щодо цього, для будь-якого іншого довільного класу, визначеного користувачем, зберігаючи певне значення, значення якого не відоме реалізації. І крім того, значення довільних об’єктів класу не можна обчислити під час компіляції.

Планується розглянути можливість дозволити літеральні типи класів як типи параметрів шаблону для post-C ++ 0x, які ініціалізуються константними виразами. Їх можна спотворити шляхом рекурсивного спотворення членів даних відповідно до їх значень (для базових класів, наприклад, ми можемо застосувати обхід з глибини, зліва направо). Але це точно не буде працювати для довільних класів.


10

Нетиповий аргумент шаблону, наданий у списку аргументів шаблону, є виразом, значення якого можна визначити під час компіляції. Такими аргументами повинні бути:

постійні вирази, адреси функцій або об'єктів із зовнішнім зв'язком або адреси статичних членів класу.

Крім того, рядкові літерали - це об’єкти з внутрішнім зв’язком, тому ви не можете використовувати їх як аргументи шаблону. Ви також не можете використовувати глобальний вказівник. Літерали з плаваючою комою не допускаються, враховуючи очевидну можливість помилок округлення.


Ви використовували цитату, так що джерело? Я хотів би підтримати його, якщо у нього є джерело для цитати.
Габріель Стейплз,
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.