Параметры шаблона без типа


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

74

Это не разрешено.

Однако это разрешено:

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

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

См. §14.1 / 6,7,8 в стандарте C ++ (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, который спрашивал, почему существует такое поведение; этот ответ просто повторяет пример OP без каких-либо объяснений.
Quuxplusone

1
Я опаздываю на вечеринку, похоже, что умные указатели тоже не работают
Николас Хамфри

28

Вам нужно уметь искажать аргументы шаблона

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

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

Теперь импл должен придумать уникальную последовательность символов для std::string или, если на то пошло, любого другого произвольного определяемого пользователем класса, хранящего конкретное значение, значение которого не известно реализации. Кроме того, значение произвольных объектов класса не может быть вычислено во время компиляции.

Планируется рассмотреть возможность использования буквальных типов классов в качестве типов параметров шаблона для пост-C ++ 0x, которые инициализируются константными выражениями. Их можно изменить, рекурсивно изменяя элементы данных в соответствии с их значениями (для базовых классов, например, мы можем применить обход слева направо в глубину). Но для произвольных классов это точно не сработает.


10

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

постоянные выражения, адреса функций или объектов с внешней связью или адреса статических членов класса.

Кроме того, строковые литералы - это объекты с внутренней связью, поэтому вы не можете использовать их в качестве аргументов шаблона. Вы также не можете использовать глобальный указатель. Литералы с плавающей запятой не допускаются из-за очевидной возможности ошибок округления.


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