Может ли объект знать свою собственную константу?

С decltype и std::is_const константа переменной может быть обнаружена извне. Но также возможно, чтобы объект знал свою собственную константу? Использование должно выглядеть следующим образом:

#include <type_traits>
#include <iostream>
#include <ios>

struct Test
{
    Test() {}

    bool print() const
    {
       // does not work as is explained in https://stackoverflow.com/q/9890218/819272
       return std::is_const<decltype(*this)>::value; // <--- what will work??
    }
};

int main()
{
    Test t;
    const Test s;

    // external constness test
    std::cout << std::boolalpha << std::is_const<decltype(t)>::value << "\n";
    std::cout << std::boolalpha << std::is_const<decltype(s)>::value << "\n";

    // internal constness test
    std::cout << std::boolalpha << t.print() << "\n";
    std::cout << std::boolalpha << s.print() << "\n"; // <--- false??
}

Выход на LiveWorkSpace Как это возможно?

МОТИВАЦИЯ. Я хочу, чтобы определить, вызывается ли функция-член const в const-объекте или исходит из неконстантного объекта. Объект может, например, представляют кэш, а член - представление. Если кеш был const, можно было бы использовать оптимизированную процедуру рисования, тогда как если базовые данные были неконстантными, процедура рисования должна была бы периодически проверять, были ли данные обновлены.

ПРИМЕЧАНИЕ: связанный с этим вопрос ) спрашивает, как разбить сборку для объектов const, но я не совсем понимаю, если ответ на что подразумевает определенный НЕТ для моего вопроса. Если нет, я хочу зафиксировать константу в булевом для дальнейшего использования.

EDIT: как было указано @DanielFrey, конструктор не является хорошим местом для тестирования на константу. Как насчет функции-члена const?


ОБНОВЛЕНИЕ. Спасибо всем за исправление моего изначально некорректного вопроса и предоставление различных частей ответа (неопределенная константа конструкторов, rvaluedness of this, контекстуальный смысл const, с ретроспективным взглядом - очевидным перегрузочным трюком, который я упустил, и лазейкой с псевдонимом const, скрывающимся в тени). Для меня этот вопрос был Stackoverflow в лучшем виде. Я решил выбрать @JonathanWakely ответ, потому что он показал, как определить классы Mutable и Immutable, которые укрепляют концепцию константы, чтобы достичь того, что я хочу в надежном виде.

Ответ 1

Как утверждали другие, вы не можете определить, был ли объект объявлен как const изнутри функции-члена. Вы можете только указать, вызвана ли она в контексте const, что не то же самое.

МОТИВАЦИЯ: Я хочу, чтобы определить, вызывает ли функция-член const в объекте const или исходит из неконстантного объекта. Объект может, например, представляют кэш, а член - представление. Если кеш был const, можно было бы использовать оптимизированную процедуру рисования, тогда как если базовые данные были неконстантными, процедура рисования должна была бы периодически проверять, были ли данные обновлены.

Вы не можете сказать это достоверно.

struct A
{
  void draw() { fut = std::async(&A::do_draw, this, false); }
  void draw() const { fut = std::async(&A::do_draw, this, true); }
  void update(Data&);
private:
  void do_draw(bool this_is_const) const;
  mutable std::future<void> fut;
};

A a;
const A& ca = a;
ca.draw();           // object will think it const, but isn't
Data new_data = ...;
a.update(new_data);  // do_draw() should recheck data, but won't

Вы можете моделировать его в системе типов, определяя отдельные изменяемые и неизменные типы.

struct Base
{
  virtual ~Base();
  virtual void draw() const = 0;
protected:
  void do_draw(bool) const;
};

struct MutableCache : Base
{
  virtual void draw() const { fut = std::async(&Base::do_draw, this, false); }
  void update();
};

struct ImmutableCache : Base
{
  virtual void draw() const { fut = std::async(&Base::do_draw, this, true); }
  // no update function defined, data cannot change!
};

Теперь, если кеш создается как ImmutableCache, вы знаете, что он не может измениться, что заменяет вашу предыдущую идею объекта const. A MutableCache может измениться, поэтому необходимо проверить обновленные данные.

Ответ 2

Это невозможно для конструкторов (исходный вопрос) из-за

12.1 Конструкторы [class.ctor]

4 Конструктор не должен быть virtual (10.3) или static (9.4). Конструктор может быть вызван для объектов const, volatile или const volatile. Конструктор не должен быть объявлен const, volatile или const volatile (9.3.2). const и volatile семантика (7.1.6.1) не применяются к строящемуся объекту. Они вступают в силу, когда заканчивается конструктор для самого производного объекта (1.8). Конструктор не должен быть объявлен с помощью квалификатора ref.

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

Ответ 3

что будет работать?

Ничего не будет работать.

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

Он также не может быть определен для функций-членов (включены неконстантные функции-члены, поскольку const_cast может быть использован)

Const-ness - это свойство, которое существует в каждом сайте вызова, а не как свойство самого тела функции.

МОТИВАЦИЯ: Я хочу, чтобы определить, вызывается ли функция-член const в объекте const

Вы не можете этого сделать, но можете приблизиться...

class C
{
public:
    void func() const
    {
        std::cout << "const!";
        func_impl();
    }
    void func()
    {
        std::cout << "non-const!";
        func_impl();
    }
private:
    void func_impl() const;
};

Объект может, например, представляют кэш, а член - представление. Если кеш был const, можно было бы использовать оптимизированную процедуру рисования, тогда как если базовые данные были неконстантными, процедура рисования должна была бы периодически проверять, были ли данные обновлены.

Это было бы ненадежным использованием const, потому что const не является свойством самого объекта. Это свойство текущего контекста того, что используемый объект.

Обнаружение того, что он const в текущем контексте не говорит вам, что объект всегда обрабатывался в контексте const.

Ответ 4

Я не знаю, возможно ли это таким образом, с помощью значения члена, заданного в конструкторе, но объект может сообщить о своей константе с функциями-членами:

struct Test
{
  bool is_const() const
  {
    return(true);
  }

  bool is_const()
  {
    return(false);
  }
};

Ответ 5

Тип this (и, следовательно, *this) определяется только cv-определителем функции и не изменяется в зависимости от того, является ли фактический объект cv-квалифицированным или нет.

§9.3.2 [class.this] p1

В теле нестатической (9.3) функции-члена ключевое слово this является выражением prvalue, значением которого является адрес объекта, для которого вызывается функция. Тип this в функции-члене класса X равен X*. Если функция-член объявлена ​​const, тип this равен const X*, если объявлена ​​функция-член volatile, тип this равен volatile X*, и если функция-член объявлена ​​const volatile, тип this равен const volatile X*.

Итак, вы не можете видеть внутри функции-члена, является ли объект, на который он вызывается, const, но вы можете заставить компилятор вызывать разные функции в зависимости от const ness:

struct X{
  void f(){ /* non-const object */ }
  void f() const{ /* const object */ }
};

int main(){
  X x1;
  X const x2;
  x1.f(); // calls non-const 'f'
  x2.f(); // calls const 'f'

  // but beware:
  X const& rx = x1;
  rx.f(); // calls const 'f', because the "object" it invoked on is const
}

Остерегайтесь ограничений, изложенных в фрагменте.

Ответ 6

Это невозможно, потому что возможно, чтобы определенное значение одновременно рассматривалось как const, а не const. Рассмотрим

MyType t = ...;
MyType& nonConstRef = t;
const MyType& constRef = t;

В этот момент t имеет как const, так и неконстантную ссылку.

Ответ 7

Обнаружение константы внутри объекта невозможно, но вы утверждаете, что ваша мотивация - это & ​​hellip;

"Я хочу, чтобы определить, вызывается ли функция-член const в const-объекте или исходит из неконстантного объекта".

Хорошо, что просто, просто предоставьте перегрузку const.

Перегрузки могут относиться к общей реализации, например. следующим образом:

#include <type_traits>
#include <iostream>
using namespace std;

class Test
{
private:
    template< class CvTest >
    static void print( CvTest& o )
    {
        cout << boolalpha;
        cout << "> object says: Hey, const = " << std::is_const<CvTest>::value << "!" << endl;
    }

public:
    bool print()        { return (print( *this ), false); }
    bool print() const  { return (print( *this ), true); }
};

int main()
{
    Test t;
    const Test s;

    cout << "External constness test:" << endl;
    cout << boolalpha << is_const<decltype(t)>::value << "\n";
    cout << boolalpha << is_const<decltype(s)>::value << "\n";

    cout << endl;

    cout << "Internal constness test:" << endl;
    cout << boolalpha << t.print() << "\n";
    cout << boolalpha << s.print() << "\n";
}

Результаты:

External constness test:
false
true

Internal constness test:
> object says: Hey, const = false!
false
> object says: Hey, const = true!
true