方便的C++ struct初始化

bit*_*ask 137 c++ struct initialization

我正在尝试找到一种方便的方法来初始化'pod'C++结构.现在,考虑以下结构:

struct FooBar {
  int foo;
  float bar;
};
// just to make all examples work in C and C++:
typedef struct FooBar FooBar;
Run Code Online (Sandbox Code Playgroud)

如果我想在C(!)中方便地初始化它,我可以简单地写:

/* A */ FooBar fb = { .foo = 12, .bar = 3.4 }; // illegal C++, legal C
Run Code Online (Sandbox Code Playgroud)

请注意,我想明确地避免使用以下表示法,因为如果我将来更改结构中的任何内容,它会让我感到沮丧:

/* B */ FooBar fb = { 12, 3.4 }; // legal C++, legal C, bad style?
Run Code Online (Sandbox Code Playgroud)

为了实现与/* A */示例中相同(或至少类似)的C++ ,我将不得不实现一个愚蠢的构造函数:

FooBar::FooBar(int foo, float bar) : foo(foo), bar(bar) {}
// ->
/* C */ FooBar fb(12, 3.4);
Run Code Online (Sandbox Code Playgroud)

这对开水有好处,但不适合懒人(懒惰是好事,对吧?).此外,它几乎与/* B */示例一样糟糕,因为它没有明确说明哪个值转到哪个成员.

所以,我的问题基本上是如何/* A */在C++中实现类似或更好的东西?或者,我可以解释为什么我不想这样做(即为什么我的心理范式很糟糕).

编辑

通过方便的,我的意思也是维护非冗余.

iam*_*ind 40

因为style A在C++中不允许,你不想要style B使用如何style BX:

FooBar fb = { /*.foo=*/ 12, /*.bar=*/ 3.4 };  // :)
Run Code Online (Sandbox Code Playgroud)

至少在某种程度上有所帮助.

  • 如果我将来在`foo`和`bar`之间插入新字段,注释不会阻止结构初始化被破坏.C仍然会初始化我们想要的字段,但C++不会.这就是问题的关键 - 如何在C++中实现相同的结果.我的意思是,Python用命名参数做C,用"命名"字段做,而C++应该有一些东西,我希望. (17认同)
  • +1:它并没有真正确保正确的初始化(来自编译器POV)但确实有助于读者...虽然评论应该保持同步. (8认同)
  • 评论同步?给我一个休息时间.安全穿过窗户.重新排序参数和繁荣.使用`explicit FooBar :: FooBar(int foo,float bar)更好:foo(foo),bar(bar)`.注意**explicit**关键字.即使违反标准,在安全方面也会更好.在Clang:-Wno-c99-extensions (2认同)

iva*_*ult 15

指定的初始化将在c ++ 2a中得到支持,但您不必等待,因为它们在Clang的GCC中得到官方支持.

#include <iostream>
#include <filesystem>

struct hello_world {
    const char* hello;
    const char* world;
};

int main () 
{
    hello_world hw = {
        .hello = "hello, ",
        .world = "world!"
    };

    std::cout << hw.hello << hw.world << std::endl;
    return 0;
}
Run Code Online (Sandbox Code Playgroud)

Demo

  • 20201?天哪,我睡了很长时间! (4认同)
  • 买者自负:请记住,如果稍后将参数添加到结构体的末尾,旧的初始化仍然会在没有初始化的情况下进行静默编译。 (2认同)
  • @Catskul不。它[将被初始化](http://eel.is/c++draft/dcl.init.aggr#5.2),初始化列表为空,这将导致初始化为零。 (2认同)
  • 自 2020 年 12 月 31 日起,任何使用 Visual Studio 2019 的人都需要为“配置属性 -&gt; C/C++ -&gt; 语言”下包含的“*C++ 语言标准*”字段设置“/std:c++latest” `。这将提供对当前正在开发中的 C++20 功能的访问。C++20 尚未作为 Visual Studio 的完整和最终实现提供。 (2认同)

eye*_*ash 9

你可以使用lambda:

const FooBar fb = [&] {
    FooBar fb;
    fb.foo = 12;
    fb.bar = 3.4;
    return fb;
}();
Run Code Online (Sandbox Code Playgroud)

关于这个成语的更多信息可以在Herb Sutter的博客上找到.

  • 这种方法将字段初始化两次。一旦在构造函数中。其次是`fb.XXX = YYY`。 (2认同)

Mat*_* M. 8

你的问题有点困难,因为即使是功能:

static FooBar MakeFooBar(int foo, float bar);
Run Code Online (Sandbox Code Playgroud)

可以称为:

FooBar fb = MakeFooBar(3.4, 5);
Run Code Online (Sandbox Code Playgroud)

因为内置数字类型的促销和转化规则.(C从来没有真正强类型)

在C++中,尽管有模板和静态断言的帮助,但您可以实现的目标是:

template <typename Integer, typename Real>
FooBar MakeFooBar(Integer foo, Real bar) {
  static_assert(std::is_same<Integer, int>::value, "foo should be of type int");
  static_assert(std::is_same<Real, float>::value, "bar should be of type float");
  return { foo, bar };
}
Run Code Online (Sandbox Code Playgroud)

在C中,您可以命名参数,但是您永远不会得到更多.

另一方面,如果你想要的只是命名参数,那么你写了很多繁琐的代码:

struct FooBarMaker {
  FooBarMaker(int f): _f(f) {}
  FooBar Bar(float b) const { return FooBar(_f, b); }
  int _f;
};

static FooBarMaker Foo(int f) { return FooBarMaker(f); }

// Usage
FooBar fb = Foo(5).Bar(3.4);
Run Code Online (Sandbox Code Playgroud)

如果你愿意,你可以在类型促销保护中加油.


ral*_*nja 8

将内容提取到描述它们的函数中(基本重构):

FooBar fb = { foo(), bar() };
Run Code Online (Sandbox Code Playgroud)

我知道样式非常接近你不想使用的样式,但它可以更容易地替换常量值并解释它们(因此不需要编辑注释),如果它们改变了那样.

你可以做的另一件事(因为你是懒惰的)是使构造函数内联,所以你不必输入那么多(删除"Foobar ::"和在h和cpp文件之间切换的时间):

struct FooBar {
  FooBar(int f, float b) : foo(f), bar(b) {}
  int foo;
  float bar;
};
Run Code Online (Sandbox Code Playgroud)

  • 如果您希望做的只是能够使用一组值快速初始化结构,我强烈建议其他阅读此问题的人在此答案的底部代码片段中选择样式。 (2认同)

Mat*_*chs 6

许多编译器的C++前端(包括GCC和clang)都理解C初始化器语法.如果可以,只需使用该方法即可.

  • 哪个不符合C++标准! (16认同)
  • 我知道这是非标准的.但是如果你可以使用它,它仍然是初始化结构的最明智的方法. (5认同)
  • clang(基于llvm的c ++编译器)也支持这种语法.太糟糕了,它不是标准的一部分. (4认同)
  • 你可以保护x和y的类型使得错误的构造函数私有:`private:FooBar(float x,int y){};` (2认同)