2014-09-02 3 views
2

Я следующий код:Инициирование ссылку на константный объект

class c_int { 
public: 
    int &i; 
    c_int(int &in) : i(in) { 
    }; 
    c_int(const int &in) : i(in) { 
    }; 
}; 


int main (void) { 

    const int i = 10; 
    const c_int c(i); 

    std::cout << c.i << std::endl; 

    return 0; 
} 

Компиляция дает следующее сообщение об ошибке:

tmp.cpp:12:30: error: invalid initialization of reference of type 'int&' from ex 
presion of type 'const int' 

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

Редактировать: В идеале я хотел бы, чтобы этот объект служил для неконстантных ints; когда объект не объявлен const.

Далее Edit:

Я хотел бы использовать объект, как это также:

int main (void) { 

    int i = 10; 
    c_int c(i); // for some reason this calls the 2nd of my constructors, does name mangling not take const into account? 
    c.i = 9;  

    std::cout << i << std::endl; 

    return 0; 
} 
+2

Ваша проблема в том, что 'c_int :: i' является' int & ', и вы пытаетесь связать его с' const int'. –

ответ

2

Я бы предположил, что у вас есть два разных класса. Тот, который содержит константную ссылку, и ту, которая содержит неконстантную ссылку. Это похоже на то, как стандартные контейнеры имеют как iterator, так и const_iterator как два отдельных класса.

class c_int { 
public: 
    int &i; 
    c_int(int &in) : i(in) {}; 
}; 

class const_c_int { 
public: 
    int const &i; 
    c_int(int const &in) : i(in) {}; 
}; 

Если вы хотите, чтобы избежать повторений кода, вы могли бы сделать шаблон:

template<typename T> 
class c_generic { 
public: 
    T& i; 
    c_generic(T& in) :i(in) {} 
}; 

typedef c_generic<int> c_int; 
typedef c_generic<int const> const_c_int; 
+0

спасибо, это одно решение, оно не кажется очень сухим, хотя? – jayjay

+0

@jayjay: Я предоставил альтернативу. –

+0

@jayjay Это один из недостатков 'const' в нескольких местах - это приводит к нежелательному дублированию кода в некоторых сценариях. Да, вы можете обойти это обычно, как здесь. –

6

Проблема заключается не в том, что ваш объект const: это то, что вы пытаетесь связать константную ссылку неконстантный: это незаконно.

Вы можете связать константную ссылку на константный член:

class c_int { 
public: 
    const int &j; 
    c_int(const int &in) : j(in){ }; 
}; 


int main (void) { 

    const int i = 10; 
    c_int c(i); 

    std::cout << c.j << std::endl; 
} 

Вы можете по-прежнему использует два различных класса для const и non const объектов, или если вы действительно нужен один класс и знать, что вы делать, используйте const_cast (на самом деле не рекомендуется: любая попытка изменить объект будет иметь неопределенное поведение):

Или забыть о переходе по ссылке и и SE значение семантики:

class c_int { 
public: 
    int i; 
    c_int(int in) : i(in){ } 
}; 

Edit:

Вы все еще можете добавить конструктор принимает неконстантную ссылку для случаев, когда ИНТ аргумент не Const:

class c_int { 
public: 
    const int &j; 
    c_int(const int &in) : j(in){ }; 
    c_int(int &in) : j(in){ } 
}; 


int main (void) { 

    const int i = 10; 
    const c_int c(i); 
    std::cout << c.j << '\n'; 

    int i2 = 11; 
    const c_int d(i2); 
    std::cout << d.j << '\n'; 
} 

Live demo.

+0

спасибо, что если мне нужно также иметь копии объекта, где ссылка int не const – jayjay

+0

ах, спасибо снова должно быть яснее, хотя извините, я имел в виду свойство non-const, я хотел бы изменить int, используя объект, т.е. 'ci = 5'; (когда объект не объявлен const). – jayjay

+1

@jayjay: вы просите изменить объект 'const' через ссылку, отличную от const: это незаконно и имеет ** неопределенное поведение ** (вы можете сделать это с помощью' const_cast <> ', но это UB). Либо вы принимаете const ref, и вы никогда не изменяете объект, или вы берете неконстантные. В вашем случае я бы просто взял объект по значению (а не по ссылке), и все будет хорошо: 'c_int (int in)' – quantdev

1

Я думаю вы путаете смысл const. Ссылка всегда постоянна (независимо от того, обращается ли она к объекту const или нет): вы не можете изменить ссылку и как член класса, она должна быть инициализирована в списке инициализации конструктора.

Ситуация эквивалентна ситуации с указателями. Рассмотрим:

const int*a;    // pointer to a const int: cannot change *a, but can change a 
int*const b;    // pointer to an int, cannot change b, but can change *b 
const int*const c;  // constant pointer to a constant int: cannot change c nor *c 

int&x;     // equivalent to pointer b 
const int&y;    // equivalent to pointer c 

Ссылка всегда постоянна, поэтому второй const с указателями всегда.

1

Вы хотите тот же класс c_int работать константную ссылку и не связанные ссылки константных к int зависят от «константности» самого c_int. К сожалению, это невозможно в C++. По той же причине, например, iterator и const_iterator являются разными типами в STL. Вы можете использовать тот же метод, хотя:

class c_int { 
public: 
    int &j; 
    c_int(int &in) : j(in){ } 
}; 

class const_c_int { 
public: 
    const int &j; 
    const_c_int(const int &in) : j(in){ } 
    const_c_int(const c_int &in) : j(in.j){ } 
}; 

Таким образом, вы будете иметь возможность конвертировать c_int в const_c_int, а не наоборот

+0

спасибо, вот чего я боялся, просто чтобы быть ясным, я надеялся, что «constness» свойства будет зависеть от «constonst» объекта, а не только от самого int. Но я предполагаю, что это все еще невозможно? – jayjay

+0

@jayjay вы пропустили ответ Уолтера. Ссылка - плохой пример. Представьте, что у вашего класса есть указатель. Тогда постоянство вашего класса означало бы, что вы не можете изменить этот указатель, то есть указать его на другой 'int', а не на то, что данные, на которые он указывает, не могут быть изменены. Со справочной константой вашего объекта не имеет никакого значения, поскольку вы не можете изменить ссылку, но принцип тот же. – Slava

1

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

Один из вариантов состоит в том, чтобы на самом деле иметь два разных класса: один для обертывания ints и один для упаковки const int. Аналогично тому, как iterator и const_iterator - это разные типы. Это может быть либо с помощью параметра шаблона, либо версия non-const может быть получена из версии const, или они могут быть полностью автономными.

Чтобы использовать один класс без шаблона, вам понадобится использовать функцию, возвращающую ссылку, чтобы она могла быть перегружена, независимо от того, является ли ваш объект const или нет. Кроме того, это должно быть свойство, которое проверяется во время выполнения, потому что всегда можно передать объект non-const в const. Например:

class c_int 
{ 
private: 
    int const *p_int; 
    bool is_const; 

public: 
    c_int(int &x): p_int(&x), is_const(false) {} 
    c_int(int const &x): p_int(&x), is_const(true) {} 

    int &i() 
    { 
     if (is_const) 
       throw std::runtime_error("non-const access for const int"); 
     return const_cast<int &>(*p_int); 
    } 

    int const &i() const { return *p_int; } 
}; 

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

Смежные вопросы