【问题标题】:If class A modifies its construction parameters, can I initialize const A's with const parameters?如果类 A 修改了它的构造参数,我可以用 const 参数初始化 const A 吗?
【发布时间】:2016-04-21 14:57:53
【问题描述】:

假设我有

class A final { 
    int& ir; 
public:
    A(int& x) : ir(x) { }
    void set(int y) { ir = y; } // non-const method!
    int get() const { return ir; }
};

和

const int i;

显然我不能拥有

 A a(i);

因为这会破坏 constness。但我也不能拥有

 const A a(i);

尽管事实上这不会破坏 constness。 C++ 不支持“const-only”ctors,例如在这种情况下,需要const int&。有没有办法让const A a 包装对i 的引用 - 除了

A a(const_cast<int &>(i))

?

【问题讨论】:

  • 您能描述一下您想要实现的目标吗?
  • “尽管没有打破 constness”?我不明白。之后您可以写信给a.ir。我会说这很不安全。
  • 想到的是class A { variant&lt;int&amp;, int const&amp;&gt; v; A(const int&amp;a):a(a) { } A(int &amp;a):a(a) { } };。不确定这是否按原样工作,但可能是一个开始。然后使用[](auto &amp;&amp;a){} 访问a.v
  • @JohannesSchaub-litb:编辑希望更清晰。注意 ir 是私有的。你的建议很有趣,我正在考虑。
  • @paceholder:见编辑。 A 是一种 int 的外观(实际上我有一个更复杂的外观)。如果该 int 是非常量的,我应该能够构建一个可以改变其支持 int 的外观,但也可以构建一个“相同”的外观 const,并带有一个 const int 支持它。似乎 C++ 几乎迫使我选择或。

标签: c++ constructor constants const-correctness const-cast


【解决方案1】:

“Const”表示对象在构造函数结束和析构函数开始之间保持不变(在构造过程中,您必须能够更改对象)。 C++ 没有 const 对象首选的“准备构造函数”。

您可以通过应用boost::variant 来尝试此解决方法。这在编译时不是完全类型安全的,但至少在运行时通过抛出异常来检测错误。

#include <boost/variant.hpp>
#include <iostream>

class R {
  boost::variant<int&, const int&> v;
public:
  R(int&v):v(v) { }
  R(const int&v):v(v) { }

  // works with both
  int get() const { 
     return boost::apply_visitor( 
        [](int x){return x;}, v); 
  }

  // only works for non-const A. If at construction, a const
  // int was passed, throws an error at runtime
  void set(int x) {
     boost::get<int&>(v) = x;
  }
};


int main() {
  int a = 0;
  const int b = a;
  R r1(a);
  R r2(b);
  const R r3(a);
  std::cout << r1.get() << r2.get() << r3.get();
  // r3.set(1); // compile error
  r1.set(1); // works
  r2.set(1); // runtime error
}

【讨论】:

  • 1.标准中不是变体吗? 2. apply_visitor 有多慢?
  • @einpoklum 我相信变体在 C++17 中(我可能是错的)。绝对不是 C++11 或 C++14。 apply_visitor 基本上是一个 switch 语句(也许在 boost impl 中更花哨,我不知道)。但是由于这里的两个 switch case 最终都执行了相同的代码(只是一个 int 的移动),我希望编译器折叠它们并内联整个 apply_visitor。如果有疑问,请忽略它,直到它导致性能问题:)
【解决方案2】:

C++ 没有完全常量类的概念,如果引用 int &amp; 仅用作 const int &amp;,编译器可能无法执行。所以基本上,你做不到。

【讨论】:

    【解决方案3】:

    这很奇怪,但是

    class A final {
        union{
        int& ir;
        const int &cir;
        };
    public:
        A(int& x) : ir(x) { }
        A(const int& x) : cir(x) { }
        void set(int y) { ir = y; } // non-const method!
        int get() const { return ir; }
    };
    
    int main(int argc, char *argv[])
    {
        const int cv = 8;
        int v = 6;
        A a( cv );
        std::cout << a.get() << std::endl;
        a.set( v );
        std::cout << a.get() << std::endl;
        return 0; 
    }
    

    你的 set 和 get 方法也对值而不是引用进行操作,所以看起来你做错了什么

    【讨论】:

    • 如果标准允许联合内的引用数据成员,我几乎会赞成它的简单性。
    • @user2079303 这也行不通:他的示例将编译并产生未定义的行为,因为没有什么能阻止他调用a.set(v)。您需要记住您使用的构造函数,例如使用 boost::variant 或手动存储它。
    • @JohannesSchaub-litb 好点。在这种情况下,这种简单性似乎不值得。
    • @JohannesSchaub-litb 你能解释一下你在谈论什么样的不确定行为吗?
    • @jeka 修改“const int”会导致未定义的行为。
    【解决方案4】:

    这不是我编写代码的方式,但它以某种方式工作。 如果您从 const 变量构造模板类,则 set() 函数也被定义为 const 并且不会更改状态。通过从非常量变量构造,set函数可以改变状态。

    #include <iostream>
    #include <string>
    #include <type_traits>
    
    template<typename T>
    class A
    {
        T &_i;
    public:
        A(T &i) : _i(i) {}
    
       template <typename D = T,
                 typename = std::enable_if_t<std::is_const<D>::value
                                            >
                >
        void set(T i) const { std::cout << "Do nothing on set" << std::endl; }
    
       template <typename D = T,
                 typename = std::enable_if_t<!std::is_const<D>::value>
                >    
        void set(T i) { std::cout << "Do something on set" << std::endl; }
    };
    
    int main()
    {
        {
            std::cout << "Construct from NON const variable" << std::endl;
            int b = 5;
    
            A<decltype(b)> a(b);   
    
            a.set(3);
        }
    
       {
            std::cout << "Construct from const variable" << std::endl;
            int const  b = 5;
    
            A<decltype(b)> a(b);      
    
            a.set(3);
        }
    }
    

    打印出来:

    Construct from NON const variable
    Do something on set
    Construct from const variable
    Do nothing on set
    

    【讨论】:

      【解决方案5】:
      class A { 
          const int& ir; //  <<-- it's a const int. 
          /* ... */
      public:
          A(int& x) : ir(x) { }
      };
      

      【讨论】:

      • 不是我的意思,我想要一些非常量 A 和一些 const A,并且能够使用 const int&amp;'s 构造 const A
      猜你喜欢
      • 1970-01-01
      • 2014-08-08
      • 2020-09-17
      • 1970-01-01
      • 1970-01-01
      • 2014-01-21
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多