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

Почему std:: is_const:: значение 'false', хотя T value_type является const?

#include <type_traits>

struct foo;
int main()
{
    const foo *bar;

    static_assert(std::is_const<decltype(*bar)>::value,
                  "expected const but this is non-const!");
}

Это приводит к непредвиденному сбою static_assert. Это несколько похоже на этот вопрос на ссылки const, но не совсем то же самое.

В моем случае разыменование bar должно давать экземпляр const foo как его тип, но все же std::is_const говорит иначе.

4b9b3361

Ответ 1

Вскоре это потому, что ссылка или указатель на тип const не является типом const.
Обратите внимание, что decltype(*bar) не const foo, это const foo &, и они действительно разные звери.


Рассмотрим приведенный ниже пример здесь:

std::cout << std::is_const<const int *>::value << '\n'; // false
std::cout << std::is_const<int * const>::value << '\n'; // true

Мы видим, что std::is_const<const int *>::value ложно и std::is_const<int * const>::value истинно.
Это потому, что в const int * тип является указателем на что-то const, это не тип const, как предполагалось is_const (и стандартом фактически). В int * const спецификатор const применяется к типу указателя, а не к указанному, поэтому тип является константным, независимо от того, что он указывает.
Что-то подобное применимо для const foo &, то есть ссылки на что-то const.

Вместо этого вы можете решить эту проблему:

static_assert(std::is_const<std::remove_reference_t<decltype(*bar)>>::value, "expected const but this is non-const!");

Или даже это, для вас не нужно делать *bar на самом деле:

static_assert(std::is_const<std::remove_pointer_t<decltype(bar)>>::value, "expected const but this is non-const!");

В этом случае, удалив указатель/ссылку с помощью remove_pointer_t/remove_reference_t, ваш тип станет const foo, это фактически тип const.


В качестве дополнительной заметки в приведенном выше примере используются свойства типа С++ 14-ish std::remove_reference_t и std::remove_pointer_t.
Вы можете легко превратить эти строки кода в С++ 11, как следует:

static_assert(std::is_const<typename std::remove_pointer<decltype(bar)>:: type>::value, "expected const but this is non-const!");

Стоит упомянуть несколько комментариев к ответу, чтобы дать более подробную информацию:

  • Спасибо @DanielFischer за вопрос:

    Есть ли короткое объяснение, почему decltype(*bar) есть const foo&, а не const foo?

    Я не юрист по языку, но я предполагаю, что это можно сделать из [expr.unary.op]/1 (выделение мой):

    Оператор унарного * выполняет косвенное обращение: выражение, к которому оно применяется, должно быть указателем на тип объекта или указателем на тип функции , а результатом является значение lvalue, ссылаясь на объект или функцию, на которые указывает выражение.

    И [dcl.type.simple]/4.4 (основное внимание):

    в противном случае , если e является lvalue, decltype (e) является T &, где T - тип e;

    Оба ссылаются на рабочий проект.

  • Спасибо @LightnessRacesInOrbit за комментарий. Обратите внимание, что decltype(*bar) является const foo & - смешной С++ quirk из decltype, так как *bar не const foo &.