初始化常量对象中的引用

Initialising a reference in a const object

本文关键字:引用 对象 常量 初始化      更新时间:2023-10-16

我有以下代码:

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对象内的引用?

编辑:理想情况下,我希望这个对象也能用于非常量;当对象未声明为const时。

进一步编辑:

我也希望使用这样的对象:

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;
}

问题不在于您的对象是const:而是您试图将常量引用绑定到非常量引用:这是非法的。

您可以将const引用绑定到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;
}

您仍然可以为constnon const对象使用两个不同的类,或者如果您确实需要一个类并且知道自己在做什么,请使用const_cast(非常不鼓励:任何修改对象的尝试都将是未定义的行为):

class c_int {
public:
  int& i;
  c_int(int& in) : i(in){ }
  c_int(const int& in) : i(const_cast<int&>(in)){ } // Very wrong
};

或者,忘记通过引用传递并使用值语义:

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

编辑:

对于int参数不是const:的情况,您仍然可以添加一个使用非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';  
}

现场演示

我建议您有两个不同的类。一个保存常量引用,另一个保存非常量引用。这类似于标准容器将iteratorconst_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;

您希望同一类c_int作为常量引用,而对int的非常量引用取决于c_int本身的"constness"。不幸的是,这在C++中是不可能的。由于相同的原因,例如iteratorconst_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,但不能将其转换为

引用成员必须是或不是constconst对象的构造函数不可能与非const对象的构造函数不同。

一种选择是实际上有两个不同的类,一个用于包装int,另一个用于封装const。类似于CCD_ 16和CCD_。这可以通过模板参数,也可以从常量版本派生非常量版本,或者它们可以完全独立。

要使用单个非模板类,您需要使用一个返回引用的函数,以便根据对象是否为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; }
};

可以有一个默认的构造函数,它什么都不做,然后使用一个函数来设置可变的类成员,但这样就失去了构造函数的好处。

我认为您混淆了const的含义。引用始终是常量(无论是否从const对象访问):您不能更改引用,并且作为类成员,它必须在构造函数的initializer列表中初始化。

这种情况与指针的情况相当。考虑:

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总是打开的。