Проверьте, объявлен ли объект const

Я хотел бы прервать компиляцию, если объект объявлен константным.

Не работает следующее:

#include <type_traits>

struct A {

    A() : v(0)
    {
        static_assert( ! std::is_const<decltype(*this)>::value, "declared as const" );
    }

    int& AccessValue() const
    {
        return const_cast< int& >( v );
    }

    int v;
};

int main()
{
    A a1; // ok, this compiles
    const A a2; // no, this break the compilation

    a1.AccessValue() = 5; // ok
    a2.AccessValue() = 6; // OPS
}

Итак, есть ли способ прервать компиляцию, если объект этого типа объявлен константным?


person BЈовић    schedule 27.03.2012    source источник
comment
Я сомневаюсь, что это возможно. Чего вы хотите достичь?   -  person Alexandre C.    schedule 27.03.2012
comment
Если кто-то получает const A, но не должен, компилятор в конечном итоге выдаст ошибки, в основном говорящие о том же (объявленном как const), поэтому вам действительно не нужно проверять такие вещи.   -  person Shahbaz    schedule 27.03.2012
comment
@АлександрК. Использование const_cast для перехода от константной ссылки к неконстантной ссылке является UB, если только объект не объявлен неконстантным. Мне нужно использовать const_cast, но хотелось бы, чтобы люди не объявляли объекты этого типа константными.   -  person BЈовић    schedule 27.03.2012
comment
@VJovic Если бы это было возможно, я думаю, компиляторы уже проверили бы это, поскольку это очень полезная и очевидная диагностика, которую нужно выполнить до const_cast.   -  person Konrad Rudolph    schedule 27.03.2012
comment
@VJovic: Вам, вероятно, следует перенаправить свои усилия, чтобы избежать необходимости в const_cast. То есть лучше задать следующие вопросы: зачем вам нужен const_cast? Как можно избежать использования const_cast?   -  person David Rodríguez - dribeas    schedule 27.03.2012
comment
Я изменил свой пример, чтобы продемонстрировать, что я имею в виду. Вместо int должен быть какой-то сложный класс. Если вы думаете, что это невозможно, то это должен быть ответ, нет?   -  person BЈовић    schedule 27.03.2012
comment
@VJovic: разве создание i mutable не дало бы того, чего вы хотите, без актерского состава?   -  person Mat    schedule 27.03.2012
comment
@Mat Я не уверен, является ли изменение значения с помощью метода AccessTheValue() UB или нет. РЕДАКТИРОВАТЬ: Нашел [dcl.type.cv]/4, что можно изменить переменную метода, объявленную как изменяемую. Спасибо   -  person BЈовић    schedule 27.03.2012
comment
@VJovic: как сказал Mat, это не UB, если i это mutable.   -  person Matthieu M.    schedule 27.03.2012


Ответы (3)


Вы направляетесь не в ту сторону.

Тип this определяется исключительно сигнатурой метода, в котором вы его используете. То есть this всегда имеет тип cv T* const, где cv соответствует квалификаторам CV метода.

Следовательно, в конструкторе this — это просто T* const.


const_cast - это запах кода, обычно используемый только при работе с const-сломанными устаревшими библиотеками... или (иногда) чтобы избежать нарушения DRY. В новом коде вам не нужно его использовать.

Вам остается выбор:

  • сделать AccessValue неконстантным, так как это не
  • объявить i как mutable.

Я бы посоветовал выбрать первое решение. Отдавать дескриптор атрибута private уже плохо (нарушает инкапсуляцию), не нужно также нарушать const корректность.

person Matthieu M.    schedule 27.03.2012
comment
this никогда не бывает U const. Это rvalue. const не имеет смысла для значений r, не являющихся объектами, поэтому не существует значений r, не относящихся к классу/массиву, такого типа. - person Johannes Schaub - litb; 28.03.2012
comment
@JohannesSchaub-litb: это, я думаю, юридический подход. На практике определяется. Возможно, потому что это помогает компилятору диагностировать неправильно сформированные вызовы? (избегая добавления лишней логики для this) - person Matthieu M.; 28.03.2012
comment
если вы передадите это шаблону и выведете его тип, а gcc выведет его как U const, вы не только получите запутанную диагностику (кстати, диагностика, которая говорит, что это имеет тип U const, не означает, что он действительно имеет этот тип), но реальная ошибка . Некоторые компиляторы также говорят о выражениях типа U и когда они хотят сообщить пользователю, что это lvalue типа U. - person Johannes Schaub - litb; 28.03.2012
comment
@JohannesSchaub-litb: так что следует учитывать ошибку в gcc 4.3.4 и gcc 4.5.2 ? - person Matthieu M.; 28.03.2012
comment
@MatthieuM.: Да, все они не должны ничего печатать. Компилятор должен реализовать this, заменив его на (__this + 0), где __this — фактическое значение this. + 0 делает его rvalue. (Джоханнес показал мне эту идею.) - person GManNickG; 29.03.2012
comment
@GManNickG: Я думаю, что на самом деле неправильно истолковал комментарий Йоханнеса (я часто так делаю). Для this имеет смысл указывать на const или volatile объекты, что не имеет смысла, так это то, что this сам по себе является const. И мои примеры никогда не касались этого случая. Судя по всему, gcc (по крайней мере) правильно обрабатывает его как rvalue: ideone.com/qfs7h - person Matthieu M.; 29.03.2012
comment
@MatthieuM.: О, и я даже неправильно прочитал ваши тесты. Что ж, я рад, что теперь мы оба в порядке. :) - person GManNickG; 29.03.2012

Для вашего конкретного примера создание i mutable позволит достичь вашей цели:

int& AccessValue() const
{
    return v;
}

mutable int v;

Это из §7.1.6.1/4 [dcl.type.cv]:

За исключением того, что любой член класса, объявленный изменяемым (7.1.1), может быть изменен, любая попытка изменить константный объект во время его существования (3.8) приводит к неопределенному поведению.

Обратите внимание, что вы не можете изменить v с помощью указателя на элемент в константном объекте - §5.5/5 проекта n3290 [expr.mptr.oper]:

[Примечание: невозможно использовать указатель на элемент, который ссылается на изменяемый элемент, для изменения объекта класса const. Например,

struct S {
S() : i(0) { }
mutable int i;
};

void f()
{
const S cs;
int S::* pm = &S::i; // pm refers to mutable member S::i
cs.*pm = 88;         // ill-formed: cs is a const object
}

— примечание в конце]

person Mat    schedule 27.03.2012
comment
Но у меня нет указателя на переменную-член. Поэтому применяется [7.1.6.1/5, последний c++11], там разрешено его менять. есть даже пример - person BЈовић; 27.03.2012
comment
Да, изменяемый член работает. Я просто указал, что есть некоторые вещи, которые вы не можете сделать, даже если член является изменчивым - используя указатель на член конкретно. Но пока вы этого не делаете, это хорошо. - person Mat; 27.03.2012

у вас может быть несколько переменных, относящихся к одному и тому же объекту, некоторые изменяемые и некоторые константные. Например:

A a1;
const A &a2 = a1;
A * const pa = &a1;
f(a1);
....
void f(const A &a);

должны ли они быть разрешены в вашем случае? Преобразование из mutable в const является неявным, а обратное — нет. Может быть, если вы приведете пример, поможет.

РЕДАКТИРОВАТЬ: (в ответ на измененный код) с константным объектом вы можете вызывать только константную функцию-член. почему бы не иметь:

int& AccessValue()
{
    return v;
}

компилятор с жалобой, если вы вызываете AccessValue для неконстантного объекта.

person Marius    schedule 27.03.2012
comment
Изменил мой пример. Надеюсь, сейчас лучше - person BЈовић; 27.03.2012