这是否太不可变而不能使用const
Is this too non-mutable to const?
基本上,我有以下情况。注:void*
用于表示任意数据,在实际应用中是强类型的。
class A
{
public:
//uses intermediate buffer but doesn't change outward behavior
//intermediate buffer is expensive to fill
void foo(void* input_data);
//uses intermediate buffer and DOES explicitly change something internally
//intermediate buffer is expensive to fill
void bar(void* input_data);
//if input_data hasn't changed since foo, we can totally reuse what happened in foo
//I cannot check if the data is equal quickly, so I allow the user to pass in the
//assertion (put the honerous on them, explicitly tell them in the comments
//that this is dangerous to do, ect)
void bar(void* input_data, bool reuse_intermediate);
private:
void* intermediate_buffer_;
void* something_;
};
所以为了const的正确性,intermediate_buffer_永远不会暴露,所以它符合使用mutable
变量的定义。如果我从未重用过这个缓冲区,或者我在使用缓存的值之前检查了相等的input_data,那将是故事的结尾,但是由于reuse_intermediate,我觉得我一半暴露了它,所以我不确定以下内容是否有意义。
class A
{
public:
//uses intermediate buffer but doesn't change something
//intermediate buffer is expensive to fill
void foo(void* input_data) const;
//uses intermediate buffer and DOES explicitly change something internally
//intermediate buffer is expensive to fill
void bar(void* input_data);
//if input_data hasn't changed since foo, we can totally reuse what happened in foo
//I cannot check if the data is equal quickly, so I allow the user to pass in the
//assertion (put the honerous on them, explicitly tell them in the comments
//that this is dangerous to do, ect)
void bar(void* input_data, bool reuse_intermediate);
//an example of where this would save a bunch of computation, though
//cases outside the class can also happen
void foobar(void* input_data)
{
foo(input_data);
bar(input_data,true);
}
private:
mutable void* intermediate_buffer_;
void* something_;
};
想法吗?
我认为这是mutable
的不当使用。在我的经验中,mutable
用于辅助私有成员变量,这些变量本质上不能声明为const,但不会修改公共接口的"概念上的const"。
以互斥对象成员变量和'线程安全的getter'为例:
class Getter {
public:
Getter( int d, Mutex & m ) : guard_( m ), data_( d ) { };
int get( ) const { Lock l(guard_); return data_; };
private:
mutable Mutex guard_;
const int data_;
};
这里的要点是,声明为mutable
的数据(在本例中是守卫)确实改变了(它被锁定和解锁),但从用户的角度来看,这对constness没有影响。最终,尽管有可变互斥锁,您仍然不能更改const data_成员变量,并且编译器强制执行。
在您的情况下,您确实希望intermediate_buffer为const,但您通过声明它为可变的方式显式地告诉编译器它不是。结果是您可以更改数据,而编译器不能对它做任何事情。
看到区别了吗?
如果你真的想让接口符合const协议,就像这样让它显式:
class A {
public:
A( void* input_data );// I assume this deep copies.
void foo() const;
void bar();
private:
const void* intermediate_buffer_;
void* something_;
};
现在责任是真正的在用户身上,由编译器强制执行,不管注释说什么,也不使用任何可变的。如果他们知道input_data已经改变,他们将不得不创建一个新的,最好是const。
不将中间对象隐藏在const对象中,而是公开它并让foo
和bar
返回一个副本。您非常明确地表明中间对象是共享的,并提供了一种新功能,可以同时保存多个中间对象。如果你想隐藏实现细节,你可以公开一个空类,并使中间对象成为这个基类的子类。
class A
{
public:
class Intermediate
{
//empty
};
//uses intermediate buffer but doesn't change outward behavior
//intermediate buffer is expensive to fill
//if cache_ptr is NULL the intermediate buffer is discarded
void foo(void* input_data, Intermediate** cache_ptr = NULL) const;
//uses intermediate buffer and DOES explicitly change something internally
//intermediate buffer is expensive to fill
void bar(void* input_data, Intermediate** cache_ptr = NULL);
private:
class IntermediateImpl : public Intermediate
{
//...
};
void* something_;
};
直接回答你的问题。如果函数foo是const,在任何时候调用它都不应该改变下一个操作的结果。
例如:A a(some_big_data);
a.bar(some_big_data, true);
应该给出与
完全相同的结果(不包括性能差异)A a(some_big_data);
a.foo(some_different_big_data);
a.bar(some_big_data, true);
由于foo是const
,用户期望结果是相同的。如果是这种情况,那么将缓冲区设置为mutable
是合理的。否则,很可能是错误的。
希望这对您有所帮助
免责声明:我的回答并不是在提倡使用void*
,我希望这只是为了演示目的,您实际上不需要自己使用它。
如果重用相同的输入数据可以节省大量的计算,那么将input_data
设为成员变量。
class A
{
public:
A(void * input_data)
{
set_input_data(input_data);
}
//Note, expensive operation
void set_input_data(void* input_data)
{
this->input_data = input_data;
fill_intermediate_buffer();
}
void foo() const;
void bar() const;
private:
void * input_data;
void * intermediate_buffer;
void * something;
};
这显然只是一个大纲,没有更多关于input_data
, intermediate_buffer
和something
是什么的细节,或者它们是如何使用或共享的,很多细节将会缺失。我肯定会从实现中删除指针并使用std::vector
。对于input_data
尤其如此,您可能希望在其中存储传入缓冲区的副本。考虑:
A a(input);
//modifiy input
a.foo();
使用不匹配的input_data/intermediate_buffer
对可能会得到错误的结果。
还要注意,如果您实际上不需要foo
和bar
的input_data
,那么您可以从类中删除void* input_data
,但仍然保留引用它的构造函数和setter。
我想说您使用mutable
确实有一定的意义-但这是不正确的,而且可能是危险的。如果你创建一个函数const
,它就需要这样。
想象一下,如果你在一个多线程程序中使用你的类,有几个线程在同一个类的实例上操作——例如:
thread1:
while(1) {
(...) //do some work
sharedA->foo(thread1Data);
sharedA->bar(thread1Data, true);
(...) //do some more work
sharedA->bar(thread1Data, true);
(...) //do even more work
}
thread2:
while(1) {
(...) //do some different work, maybe wait for an event
sharedA->foo(thread2Data);
(...) //sleep for a bit
}
由于thread2正在调用const
函数,它不应该对从thread1调用bar
的输出有任何影响-但如果时机正确(或错误!)它会-由于这些原因,我想说在这种情况下使用mutable
使foo
成为const
是错误的,即使你只从单个线程使用类。
既然您不能检查input_data
是否等价,那么您也许可以对它进行加密散列并将其用于比较。这将消除对reuse_intermediate
标志的需要。
mutable用于覆盖变量或数据成员的const-ness。例子:
Class A
{
public:
int m;
}
void foo(const A& a);
在上面的例子中,函数foo可以修改a.m
,即使你传递了一个const引用。在你的例子中,我不认为你需要使用可变的-它会导致非常糟糕的设计-因为你将写入缓冲区。
- 递归模板化函数不能分配给具有常量限定类型"const tt &"的变量"state"
- 需要帮助在 c++ 中将字符串转换为字符 ----错误 "const char *" 类型的值不能用于初始化 "char" 类型的实体
- 为什么我不能在返回 const 的布尔函数中为类成员变量赋值?C++
- 为什么通过指针编译时不能分配 const 初始化
- 类型为 "Bucket&"(未限定的 const 限定)的引用不能使用 "SortedList." 类型的值进行初始化 如何修复此错误?
- "const wchar_t *" 类型的值不能用于初始化类型 "const PWSTR" 的实体
- Visual Studio C++:不能使用类型为 "const wchar_t *" 的值来初始化类型为 "TCHAR *" 的实体
- 不能使这种类型的"void(C::* volatile)(int) const "在参考手册C++示
- 为什么我不能声明一个 constexpr 本地,而一个 const 一个工作?
- 类型"const char[2]"的值不能隐式转换为"int"错误C++
- 为什么 std::unordered_map 不能与 const std::string 键一起使用?
- 为什么我不能使用与"常量字符*"相同的"const int*"创建一个 int 数组?
- 使用按引用调用时,不能在没有对象的情况下调用成员函数 const
- 为什么带有 const 关键字的构造函数可以工作,而没有它就不能工作?
- 为什么 vector::erase 不能在带有 const 的类元素上工作
- 为什么不能在模板函数中向局部变量添加低级 const 类型
- 不能将 "const char*" 类型的值分配给类型 "int" 的实体
- 不能将 "const char *" 类型的值分配给类型为 "LPSTR" 的实体
- std::initializer_list<int const> 不能从 std::initializer_list <int>构造
- 为什么 boost::interprocess::managed_shared_ptr 到 non-const 不能转换