10

可以从外部检测变量的和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 对象的构建,但我不太明白这个问题的答案是否意味着我的问题的明确否定。如果没有,我想在布尔值中捕获常量以供进一步使用。

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


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

4

7 回答 7

6

构造函数(原始问题)是不可能的,因为

12.1 构造函数 [class.ctor]

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

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

于 2013-03-07T19:07:09.327 回答
5

正如其他人所说,您无法判断一个对象是否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”对象的想法。AMutableCache可以更改,因此需要检查刷新的数据。

于 2013-03-07T19:50:23.193 回答
4

什么会起作用?

什么都行不通。

运行其构造函数的对象是neverconst。它只能在构造函数运行后才const分配给变量。

也无法确定成员函数(包括非 const 成员函数,因为const_cast可能已使用)

常量是存在于每个调用站点的属性,而不是函数体本身的属性。

动机:我希望能够检测是否在 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上下文中被处理。

于 2013-03-07T19:03:33.717 回答
3

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

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

  bool is_const()
  {
    return(false);
  }
};
于 2013-03-07T19:14:40.440 回答
3

this(以及因此)的类型*this完全由函数的 cv 限定符确定,并且不会根据实际对象是否为 cv 限定而改变。

§9.3.2 [class.this] p1

在非静态 (9.3) 成员函数的主体中,关键字this是纯右值表达式,其值是调用该函数的对象的地址。类的this成员函数中的类型XX*如果声明了成员函数,则为is的const类型thisconst X*,如果声明了成员函数,则为is的volatile类型,如果声明了成员函数,则为is的类型。thisvolatile X*const volatilethisconst volatile X*

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

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's invoked on is const
}

不过,请注意片段中列出的限制。

于 2013-03-07T19:28:04.170 回答
2

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

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

此时t有一个const和一个非常量引用。

于 2013-03-07T19:03:17.033 回答
2

检测对象内的 constness 是不可能的,但你说你的动机是……

“我希望能够检测 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";
}

结果:

外部常数测试:
错误的
真的

内部常数测试:
> 对象说:嘿,const = false!
错误的
> 对象说:嘿,const = true!
真的
于 2013-03-07T19:38:21.770 回答