对象能知道自己的常量吗?

时间:2013-03-07 19:00:04

标签: c++ reflection c++11 typetraits const-correctness

使用decltypestd::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,则可能使用优化的绘制例程,而如果底层数据是非const,则绘制例程需要定期检查数据是否刷新。

注意:相关的question询问如何打破const对象的构建,但我不太明白,如果答案对我的问题暗示肯定的NO。如果没有,我想在布尔值中捕获constness以供进一步使用。

编辑:正如@DanielFrey指出的那样,构造函数不是测试constness的好地方。 const成员函数怎么样?


更新:感谢大家纠正我最初的不适定问题并提供答案的各个部分(构造函数的定义不明确的常量,this的rvaluedness,上下文的含义const,后见之明 - 明显的过载技巧,我忽略了,而const参考别名隐藏在阴影中的漏洞)。对我来说,这个问题是Stackoverflow处于最佳状态。我决定选择@ JonathanWakely的答案,因为它展示了如何定义MutableImmutable类来加强constness概念,以便以万无一失的方式实现我想要的目标。

7 个答案:

答案 0 :(得分:6)

由于

,构造函数(原始问题)不可能
  

12.1构造函数[class.ctor]

     

4 构造函数不应是virtual(10.3)或static(9.4)。可以为constvolatileconst volatile对象调用构造函数。不应声明构造函数constvolatileconst volatile(9.3.2)。 constvolatile语义(7.1.6.1)不适用于正在构建的对象。它们在最派生对象(1.8)的构造函数结束时生效。不应使用ref-qualifier声明构造函数。

对于成员函数(当前问题),您可以简单地提供const和非const重载,将两者都转发到将constness作为布尔模板的(私有)方法参数。

答案 1 :(得分:5)

正如其他人所说,你无法判断某个对象是否在成员函数中被声明为const。你只能判断它是否在const上下文中被调用,这是不一样的。

  

动机:我希望能够检测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's 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”对象的想法。 MutableCache可以更改,因此需要检查刷新的数据。

答案 2 :(得分:3)

  

什么会起作用?

什么都行不通。

运行其构造函数的对象是从不const 。在构造函数运行之后,它可能只被分配给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并不会告诉您该对象始终在const上下文中处理。

答案 3 :(得分:3)

我不知道是否可能以这种方式,通过在构造函数中设置的成员值,但对象可以使用成员函数报告其常量:

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

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

答案 4 :(得分:3)

this(以及*this)的类型完全取决于函数的cv限定符,并且不会根据实际对象是否为cv限定而改变。 / p>

§9.3.2 [class.this] p1

  

在非静态(9.3)成员函数的主体中,关键字this是一个prvalue表达式,其值是调用该函数的对象的地址。类this的成员函数中X的类型为X*如果成员函数声明为const,则this的类型为const X* ,如果成员函数声明为volatile,则类型为this {1}}为volatile X*,如果成员函数声明为const volatile,则this的类型为const volatile X*

因此,您无法在成员函数的内部看到它调用的对象是const,但您可以根据{{1}使编译器调用不同的函数}岬:

const

请注意该片段中的限制。

答案 5 :(得分:2)

这是不可能的,因为特定值可能同时被视为const而不是const。考虑

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

此时t同时包含const和非const引用。

答案 6 :(得分:2)

检测对象中的常量是不可能的,但是你要说明你的动机是......

  

“我希望能够检测const对象上是调用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