拥有一个“constify”是否有意义? C++ 中的操作?

发布于 2024-09-16 03:36:55 字数 460 浏览 14 评论 0原文

C/C++ 中使用“constify”操作来生成变量 const 是否有意义?

这是一个它可能有用的示例,显然我们不想在第一行中将其声明为 const:

std::vector<int> v;
v.push_back(5);
constify v; // now it's const

目前,如果没有这种可能性,您必须引入另一个变量来获得相同的效果:

std::vector<int> v0;
v0.push_back(5);
const std::vector<int>& v = v0;

这更令人困惑,因为它在范围中添加了一个新名称,并且您需要将其作为引用以避免复制整个向量(或使用 swap?)。

Would it make sense to have a "constify" operation in C/C++ that makes a variable const?

Here is an example where it could be useful, where obviously we don't want to declare it const yet in the first line:

std::vector<int> v;
v.push_back(5);
constify v; // now it's const

Currently, without such a possibility, you'd have to introduce another variable to get the same effect:

std::vector<int> v0;
v0.push_back(5);
const std::vector<int>& v = v0;

That's more confusing since it adds a new name into the scope and you need to make it a reference to avoid copying the whole vector (or use swap?).

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(10

审判长 2024-09-23 03:36:55

坦率地说,我发现变量是否为 const 比可以更改更容易混淆。


详细说明一下:您通常想要这样做的原因是因为您无法按照您想要的方式初始化 const 变量。 std::vector 就是一个很好的例子。好吧,这一次,下一个标准引入了一个通用初始化语法,使这成为可能:

const std::vector<int> cvi = { 1, 2, 3, 4, 5, 42 }; 

但是,即使手头没有 C++1x 的东西,甚至使用不允许此初始化语法的类型,您始终可以创建一个辅助函数来执行你想要什么:

const std::vector<int>& cvi = create_my_vector();

或者,如果你想要更奇特:

const std::vector<int>& cvi = compile_time_list<1,2,3,4,5,42>::create_vector();

请注意 &。复制函数调用的结果是没有意义的,因为将右值绑定到 const 引用会延长其生命周期,直到引用生命周期结束。
当然,使用支持 C++1x 移动语义的编译器重新编译将使此类优化几乎没有必要。但是将 rvlaue 绑定到 const 引用可能仍然比移动向量更快,并且不太可能慢。
使用 C++1x,您还可以创建 lambda 函数来快速执行此操作。 C++ 提供了极其庞大的工具库。 IME,无论你多么努力地思考,其他人都应该想出另一个想法来做同样的事情。而且往往比你的更好。


然而,无论如何,IME 这个问题通常只是由于代码过多而函数太少造成的。然后它不仅适用于常量,还适用于类似的特征 - 比如引用所指的内容。
一个经典的方法是使用多个可能的流之一。相反,

int main(int argc, char* argv[])
{
  std::istream* istrm = NULL;
  std::ifstream ifs;
  if( argc > 1 )
  {
    ifs.open( argv[1] );
    if( ifs.good() ) 
      istrm = &ifs;
  }
  if( !istrm ) 
    istrm = &std::cin;

  while( istrm->good() )
  {
     // reading from *istrm implemented here
  }
  return 0;
}

只需将问题分为 1)弄清楚从哪里读​​取和 2)实际读取:

int read(std::istream& is)
{
  while( is.good() )
  {
     // reading from is implemented here
  }
  return 0;
}

int main(int argc, char* argv[])
{
  if( argc > 1 )
  {
    std::ifstream ifs( argv[1] );
    if( ifs.good() ) 
      return read(ifs);
  }
  return read(std::cin);
}

我还没有看到一个变量的真实示例,该变量不像它应有的那样恒定,而且不可能'不能通过关注点分离来解决。

Frankly, I find it less confusing if a variable is either const or not, than if this can change.


To elaborate a bit on this: The reason you usually want to do this is because you cannot initialize a const variable the way you want to. std::vector is a good example of this. Well, for once, the next standard introduces a universal initialization syntax that makes this possible:

const std::vector<int> cvi = { 1, 2, 3, 4, 5, 42 }; 

However, even without C++1x' stuff at hand, and even with types that disallow this initialization syntax, you can always create a helper function to do what you want:

const std::vector<int>& cvi = create_my_vector();

or, if you want to be fancy:

const std::vector<int>& cvi = compile_time_list<1,2,3,4,5,42>::create_vector();

Note the &. There's no point in copying the result of the function call, since binding an rvalue to a const reference extends its lifetime until the end of the reference's lifetime.
Of course, recompiling with a compiler that supports C++1x' move semantics will render such optimizations pretty much needless. But binding an rvlaue to a const reference might still be faster than moving a vector and is unlikely to be slower.
With C++1x, you might also create lambda functions doing this one the fly. C++ just provides an incredibly huge arsenal of tools. IME, no matter how hard you have thought, someone else ought to come up with yet another idea to do the same thing. And often a better one than yours.


However, IME this problem usually only comes with too much code in too few functions anyway. And then it doesn't only apply to constness, but also to similar traits - like what a reference refers to.
A classic is the use-one-of-several-possible-streams. Instead of this

int main(int argc, char* argv[])
{
  std::istream* istrm = NULL;
  std::ifstream ifs;
  if( argc > 1 )
  {
    ifs.open( argv[1] );
    if( ifs.good() ) 
      istrm = &ifs;
  }
  if( !istrm ) 
    istrm = &std::cin;

  while( istrm->good() )
  {
     // reading from *istrm implemented here
  }
  return 0;
}

just split the concerns into 1) figuring out where to read from and 2) the actual reading:

int read(std::istream& is)
{
  while( is.good() )
  {
     // reading from is implemented here
  }
  return 0;
}

int main(int argc, char* argv[])
{
  if( argc > 1 )
  {
    std::ifstream ifs( argv[1] );
    if( ifs.good() ) 
      return read(ifs);
  }
  return read(std::cin);
}

I have yet to see a real-world example of a variable that wasn't as constant as it could have been which couldn't be fixed by separating of concerns.

违心° 2024-09-23 03:36:55

您基本上是在尝试重现构造函数的效果 - 即 const 仅在构造函数完成后应用(并且仅在调用 dtor 之前)。因此,您需要的是另一个类来包装您的向量并在构造函数中对其进行初始化。一旦构造函数完成并返回,实例就变成 const(当然,假设它被定义为 const)。

C++0x 将大大改善对这种包装的要求。您将能够使用向量的大括号初始值设定项在一次操作中创建/初始化向量。其他类型将(至少可能)支持用户定义的初始值设定项来完成大致相同的事情。

You're basically trying to reproduce the effect of a constructor -- i.e., const applies only after the constructor completes (and only until the dtor is invoked). As such, what you need is another class that wraps your vector and initializes it in the ctor. Once the ctor completes and returns, the instance becomes const (assuming, of course, that it was defined to be const).

C++0x will ameliorate the requirement for wrapping like this considerably. You'll be able to use brace initializers for vectors to create/initialize the vector in one operation. Other types will (at least potentially) support user-defined initializers to accomplish roughly the same thing.

一瞬间的火花 2024-09-23 03:36:55

C++ 是静态类型的。对我来说,引入这样的操作将违反这一范式,并且会引起很多混乱。

C++ is statically typed. To me, introducing such an operation would be a violation of this paradigma and would cause much confusion.

独﹏钓一江月 2024-09-23 03:36:55

这是使用函数的好时机

#include <vector>

std::vector<int> makeVector()
{
  std::vector<int> returnValue;
  returnValue.push_back(5);
  return returnValue;
}

int main()
{
  const std::vector<int> myVector = makeVector();
}

This is a great time to use a function

#include <vector>

std::vector<int> makeVector()
{
  std::vector<int> returnValue;
  returnValue.push_back(5);
  return returnValue;
}

int main()
{
  const std::vector<int> myVector = makeVector();
}
三岁铭 2024-09-23 03:36:55

我假设您谈论的是比初始化向量(在 C++0x 中解决)更通用的东西,并且仅使用向量作为示例。

我宁愿看到它通过某种本地函数完成:(

const vector<int> values = []{
    vector<int> v;
    copy(some_other_data.begin(), some_other_data.end(), v);
    sort(v);
    return v;
}();

我可能会弄乱 C++0x 的匿名函数语法)。我可以很自然地将其读为:“根据此处描述的例程准备常量向量”。只是括号的数量稍微让我烦恼。

我可以看到,在 C++0x 对程序员来说变得更加自然之后,这段代码可能会成为 C++ 习惯用法。

(根据德曼的建议进行编辑)

I assume you're talking about something more generic than only initializing vectors (which is solved in C++0x), and use vectors only as an example.

I'd rather see it done through some kind of local functions:

const vector<int> values = []{
    vector<int> v;
    copy(some_other_data.begin(), some_other_data.end(), v);
    sort(v);
    return v;
}();

(I could mess anonymous function syntax of C++0x). This I can read quite naturally as: "prepare a const vector according to routine described here". Only amount of parentheses bother me slightly.

I can see how this code might become a C++ idiom after C++0x becomes more natural to programmers.

(edited after dehmann's suggestion)

眼泪都笑了 2024-09-23 03:36:55

我也想过这个问题。但是,恕我直言,它会造成很多混乱,这将超过它的好处。想想看,C++ 中常量的整个概念已经够令人困惑的了。

您的想法可以归结为“如何在变量初始化后使其变为只读?”。您可以通过将变量设置为类的私有成员来获得相同的效果,该变量在构造函数中初始化,并为其提供 getter 但不提供 setter。

I have thought about this too. But, IMHO, it will create a lot of confusion, which will outweigh its benefits. Come to think of it, the whole notion of constness in C++ is already confusing enough.

Your idea boils down to "How can I make a variable read-only after it has been initialized?". You can get the same effect by making your variable a private member of a class, which is initialized in the constructor, and for which you provide a getter but no setter.

失去的东西太少 2024-09-23 03:36:55

已经提到过,C++0x 使用大括号初始化器在某种程度上解决了这个问题:

const std::vector<int> values{1, 2, 3, 4, 5};

尽管这只允许初始化,并且不允许在构造函数运行后调用非 const 成员函数。 可以定义一个宏constify如下:

#define constify(type, id) \
for (type const& id##_const(id), & id(id##_const), \
    * constify_index = &id; constify_index; constify_index = 0)

可以像这样使用:

std::vector<int> v;

// v is non-const here.

constify (std::vector<int>, v) {

    // v is const here.

}

这通过设置一个执行for循环来实现以下语句或块仅一次,且 constified 变量位于循环体的本地。请注意局部 i 之前辅助变量 i_const 的声明:语句 int const& i(i)i 初始化为自身,即未初始化的值,我们希望 (i)而是引用之前声明的i,因此需要额外的级别。

如果您可以利用 C++0x 功能,则 decltype 关键字会派上用场,它允许您在调用 constify 时省略类型:

#define constify(id) \
for (decltype(id) const& id##_const(id), & id(id##_const), \
    * constify_index = &id; constify_index; constify_index = 0)

这使您可以简单地编写:

constify (v) {
    // ...
}

无论变量最初是否声明为 const,两个版本都可以工作。所以,是的,与您正在寻找的东西非常相似的东西确实是可能的,但可能完全不值得。

It's already been mentioned that C++0x solves this somewhat with brace-initialisers:

const std::vector<int> values{1, 2, 3, 4, 5};

Though this only allows for initialisation, and does not allow, for instance, invoking non-const member functions after the constructor has run. It is possible to define a macro constify as follows:

#define constify(type, id) \
for (type const& id##_const(id), & id(id##_const), \
    * constify_index = &id; constify_index; constify_index = 0)

Which can be used like so:

std::vector<int> v;

// v is non-const here.

constify (std::vector<int>, v) {

    // v is const here.

}

This works by setting up a for loop that executes the following statement or block only once, with the constified variable local to the loop body. Note the declaration of helper variable i_const before the local i: the statement int const& i(i) initialises i to itself—that is, to an uninitialised value—and we want the (i) to refer instead to the previously declared i, so an extra level is needed.

If you can make use of C++0x features, the decltype keyword comes in handy, allowing you to omit the type from invocations of constify:

#define constify(id) \
for (decltype(id) const& id##_const(id), & id(id##_const), \
    * constify_index = &id; constify_index; constify_index = 0)

Which lets you write, simply:

constify (v) {
    // ...
}

Both versions work whether the variable is initially declared const or not. So, yes, something very like what you were looking for is indeed possible, but probably altogether not worth it.

云裳 2024-09-23 03:36:55

目前,编译器知道是否为 const,因此编译器不会接受尝试更改 const 变量的程序。

如果您想创建一个 constify 运算符,则必须将其设置为变量的属性(没有进一步的关键字,每个变量),以便它可以在运行时更改。当然,每当程序尝试更改(当前)const 变量时,您都必须抛出异常,这实际上意味着对每个变量的每次写访问都必须检查 const > 财产第一。

所有这些都违背了 C++ 和所有其他静态类型语言的哲学。它还破坏了与现有库的二进制兼容性。

Currently, const or not is something that the compiler knows, so the compiler won't accept a program that tries to change a const variable.

If you wanted to make a constify operator, you would have to make this a property of the variable (without further keywords, of every variable) so it can change at runtime. And of course you would have to throw an exception whenever a program tries to change a (currently) const variable, which effectively means that every write access to every variable must check the const property first.

All this goes against the philosophy of C++ and every other statically typed language. And it breaks binary compatibility with existing libs, too.

秋日私语 2024-09-23 03:36:55

考虑以下几点:

void foo(std::vector<int> & v) 
{
  v.push_back(1);
  constify v;
}
void bar() {
  std::vector<int> test(7);
  foo(test);
  test.clear();
}

foo 中的变量 v 是否已被构造?它与 bar 中的 test 变量相同。因此,test.clear() 调用应该是无效的。我认为你真正的意思是名称是“constified”,而不是变量。

指定和实现实际上很简单: constify x; 是一个名为 x 的 const 引用的声明,它与它隐藏的变量 x 具有相同的基类型。它遵循通常的作用域规则,只是它可以在与之前的 x 声明相同的作用域中定义。

Consider the following bit:

void foo(std::vector<int> & v) 
{
  v.push_back(1);
  constify v;
}
void bar() {
  std::vector<int> test(7);
  foo(test);
  test.clear();
}

Is the variable v in foo constified? It is the same variable as test in bar. Thus, the test.clear() call should be invalid. I think that what you really meant is that the name is "constified", not the variable.

It would be actually trivial to specify and implement: constify x; is a declaration of a const reference named x, which has the same base type as the variable x it hides. It follows the usual scope rules, except that it may be defined in the same scope as the previous x declaration.

走走停停 2024-09-23 03:36:55

您可以将向量包装在类中,声明包装的向量可变,然后创建包装器的 const 实例。包装类可以更改向量,但外部调用者看到的是 const 对象

You could wrap the vector in a class , declare the wrapped vector mutable, and then make a const instance of the wrapper. The wrapping class can change the vector but external callers see a const object

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文