Подтвердить что ты не робот

Создание шаблона с ошибкой функции constexpr

У меня есть класс шаблона C, который имеет параметр типа non-type, но ссылочный шаблон для типа P:

class P {
public:
  int x;
  int y;
};

template <const P &x>
class C {
public:
  const int &f() { return x.x; }
};

Я объявила глобальную переменную типа P:

P p = {33,44};

Я также объявил функцию, которая возвращает ссылку на P:

constexpr const P &h() { return p; }

И затем попытался использовать их в следующем:

C<p> o;    // line 1
C<h()> oo; // line 2

Конечно, у меня нет проблем с первым экземпляром, кроме второго. Мой компилятор жалуется:

error: non-type template argument does not refer to any declaration

Почему так? Я не смог найти аргумент против него в норме. Я не уверен, что это точно та же проблема, что и в Вызов constexpr в аргументе шаблона по умолчанию, где обсуждался момент создания экземпляра вложенной инициализации. Здесь это скорее проблема типа, но какая? Моя функция h() возвращает ссылку на хорошо определенную переменную строго определенного типа (const P &). Я ожидал, что какая-то вставка состоится, дайте правильный результат, но это не так. Не могли бы вы сказать мне, почему?

Объявление функции в виде строки не изменяет ничего на проблему.

Эксперименты проводились с помощью Apple LLVM version 6.0 (clang-600.0.56) (based on LLVM 3.5svn. Я также пробовал с помощью g++-mp-4.8 (MacPorts gcc48 4.8.3_2) 4.8.3, и об ошибке сообщалось как:

'h()' is not a valid template argument for type 'const P&' because it is not an object with external linkage

Похоже, что мой вызов h() (который является constexpr, поэтому компилируемое время вычисляется) не рассматривается как таковое...


Я забыл сказать, что проблема такая же, если мы попробуем с другой ссылкой:

const P &pp = p;

а затем

C<pp> oo;

на этот раз первый компилятор говорит:

non-type template argument of reference type 'const P &' is not an object

а второй:

error: could not convert template argument 'pp' to 'const P &'

pp не является объектом? pp не имеет типа const P&? Ну, я могу использовать его, как он один... Я знаю, что это ссылка, но неотличимая от родной ссылки, или?

4b9b3361

Ответ 1

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

Синтаксические ограничения для указателей, ссылок и указателей на члены неудобны и не дают разумных рефакторингов. Например:

template<int *p> struct A {};
int n;
A<&n> a; // ok

constexpr int *p() { return &n; }
A<p()> b; // error

и далее говорит:

Историческая причина ограничения была, скорее всего, тем, что С++ ранее не имели достаточно сильной спецификации для константные выражения типа указателя, ссылки или указателя на член. Однако это уже не так. Статус-кво заключается в том, что для оценки такого аргумента шаблона требуется реализация, но должен отбросить результат, если он не будет равен нулю.

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

и он удалит этот раздел примечания с этим ограничением:

неназванные lvalues ​​и имена lvalues ​​без привязки

вся заметка гласит:

Временные, неназванные lvalues ​​и имена lvalues ​​без привязки неприемлемые шаблонные аргументы, когда соответствующие шаблон-параметр имеет ссылочный тип.

Обновить

Пересмотренная версия этого предложения N4268 была принята в рабочий проект в Урбане, и мы можем видеть изменения в последнем рабочем проекте N4296. Новая заметка гласит:

Временной объект не является допустимым аргументом шаблона, если соответствующий шаблон-параметр имеет ссылочный тип

Нормативный раздел 14.3.2 (temp.arg.nontype) paragraph 1, который с этим предложением сказал бы:

Для шаблона-шаблона, не относящегося к типу ссылки или типа указателя, значение константного выражения не должно упоминаться (или для указателя тип, не должен быть адресом):

  • подобъект (1.8),
  • временный объект (12.2),
  • строковый литерал (2.14.5),
  • результат выражения typeid (5.2.8) или
  • предопределенная переменная func (8.4.1).

и мы можем найти эту новую формулировку в последнем проекте стандарта N4296.

Похоже, что это изменение действительно реализовано в clang HEAD, чтобы увидеть, как ваш код работает live, используя флаг -std=c++1z. Это подразумевает, что изменение должно быть частью С++ 17, если никакие последующие изменения не изменят или не изменят его.

Ответ 2

В случае

C<h()> oo;

§14.3.2/4 ударов в:

[Примечание: Temporaries, неназванные lvalues ​​ и имена lvalues ​​без привязки не приемлемы. аргументы, когда соответствующий шаблон-параметр имеет ссылочный тип.

(акцент мой)