2017-02-02 2 views
24
#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, что является неожиданным. Это несколько похоже на this question на ссылки на const, но не совсем то же самое.Почему std :: is_const :: значение 'false', хотя значение value_type имеет const?

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

+1

'const foo * bar' создает константу _pointer_, но значение, на которое оно указывает, равно _not_ const. – Aganju

+16

@Aganju нет, это 'foo * const bar;' – greatwolf

+7

'const foo * bar' то же, что и' foo const * bar' Но отличается от 'foo * const bar' –

ответ

35

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


Рассмотрим пример, приведенный here:

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.


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

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

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

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

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

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

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

    И [dcl.type.simple]/4.4 (курсив мой):

    в противном случае, если е именующий, decltype (е) Т &, где Т представляет собой тип е;

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

  • Благодаря @LightnessRacesInOrbit для комментария. Обратите внимание, что decltype(*bar), являющийся const foo &, является смешной C++ причудой decltype, так как *bar не является const foo &.

+10

Стоит отдельно указывать imo:' decltype (* bar) 'не' const foo', это 'const foo &' –

+1

@RyanHaining, что хорошо знать, это часть, которую мне не хватало. – greatwolf

+1

@RyanHaining Добавлен в первую строку. – skypjack