Указатель Void как аргумент шаблона в С++

Не компилируется следующее:

template<void *p>
class X {
// ...
};

int r;

int main()
{
    X<&r> x;

    return 0;
}

Сообщение об ошибке

x.cc:10:6: ошибка: не удалось преобразовать аргумент шаблона '& r to 'void *

Явное использование casting & r to (void *) тоже не помогает. Сообщение об ошибке становится:

x.cc:10:14: error: невозможно преобразовать аргумент шаблона '(void *) (& r) в' void *

Какая часть стандарта определяет это поведение? Версия GCC версия gcc 5.2.1 20151003 (Ubuntu 5.2.1-21ubuntu2)

Edit:

Обратите внимание, что использование, например, int * вместо void * работает как ожидалось.

Изменить: (отвечает на себя)

Он работает не с gcc HEAD 6.0.0 20151016 (экспериментальный) при указании -std = С++ 1z, ни с неявным, ни с явным литьем в "void *".

Он работает с clang HEAD 3.8.0 (trunk 250513) и был с (по крайней мере) clang 3.6.0 (теги/RELEASE_360/final) при указании --std = С++ 1z и явно приведения к * void * ". Без явного приведения, clang жалуется следующим образом:

x.cc:10:7: ошибка: преобразование из 'int *' в 'void *' недопустимо в преобразованном постоянном выражении

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

Ответ 1

Обычно для любого указателя на void* допускается преобразование.

[С++ 11, 4.10/2] PRvalue типа "указатель на cv T", где T является тип объекта, может быть преобразован в prvalue типа "указатель на cv void". Результат преобразования "указателя на cv T" в "указатель на cv void" указывает на начало места хранения, где объект типа T, как будто объект является наиболее производным объектом (1.8) of тип T (то есть не подобъект базового класса). Значение нулевого указателя преобразуется в значение нулевого указателя для типа адресата.

Однако для аргументов шаблона непигового типа заданы определенные преобразования:

[С++ 11, 14.3.2/5] Следующие преобразования выполняются для каждого выражение, используемое в качестве аргумента шаблона, не относящегося к типу. Если не-тип template-argument не может быть преобразован в тип соответствующего шаблон-параметр, тогда программа плохо сформирована.

[...]

- для не-типа шаблона-параметра указателя типа на объект, квалификационные преобразования (4.4) и преобразование массива в указатель (4.2); если аргумент template имеет тип std:: nullptr_t, применяется преобразование нулевого указателя (4.10). [...]

Попутно, мы можем рассуждать, что это преобразование просто не разрешено.

Ответ 2

Я не могу процитировать вас главу и стихи с моей головы (правки приветствуются), но то, что вы пытаетесь сделать, не разрешено в С++.

Параметры шаблона должны быть известны во время компиляции. Указатели разрешаются только во время соединения, если:

  • они по умолчанию используют = nullptr в списке аргументов шаблона.

  • они являются указателями на функцию-члены (которые известны во время компиляции, поскольку они просто смещения).

например, это скомпилирует:

template<void * = nullptr>
class X {
    // ...
};

int r;

int main()
{
    X<nullptr> x;

    return 0;
}