是否有像C#中的C ++新声明

时间:2022-05-07 16:28:20

I wondered if there was a new declaration like in C# for C++

我想知道C#中是否有像C ++这样的新声明

C# allows you to do this and it just neatens up the code a bit:

C#允许你这样做,它只是稍微加密了代码:

FuncCall( new Foo() {
    Bar = "sausage",
    Boo = 4
} );

It's just I thought this was a bit sloppy in C++:

我只是觉得这在C ++中有点草率:

unique_ptr<Foo> foo( new Foo() );
foo.Bar = "sausage";
foo.Boo = 4;

FuncCall( move( foo ) );

Foo might look like this:

Foo可能看起来像这样:

class Foo
{
public:
    Foo();

    string Bar;
    int Boo;
}

Why am i not just placing all into construct paramters?

为什么我不只是将所有内容放入构造参数中?

Because it's stupid when you have to initalize so much:

因为当你必须这么多时,这是愚蠢的:

Foo( int width, int height, string title, string className, string thjis, stihjrjoifger gfirejgoirejgioerjgoire ) It goes on forever... Whereas i already have the properties inside my class... So was just wondering if it could be done..

Foo(int width,int height,string title,string className,string thjis,stihjrjoifger gfirejgoirejgioerjgoire)它永远存在......虽然我已经拥有了我班级内的属性......所以只是想知道它是否可以完成..

4 个解决方案

#1


You might use a lambda:

你可以使用lambda:

FuncCall( []{ Foo f; f.Bar = "sausage"; f.Boo = 4; return f; }() );

Live example

#2


Foo has to have a constructor or be an aggregate type:

Foo必须有一个构造函数或者是一个聚合类型:

class Foo {
  std::string bar;
  int boo;
public:
  Foo(std::string s, int i) : bar(std::move(s)), boo(i) {}
};

void FuncCall(std::unique_ptr<Foo> ptr) {}

int main() {
  FuncCall(make_unique<Foo>("sausage", 4));
}

struct Foo {
  std::string bar;
  int boo;
};

void FuncCall(std::unique_ptr<Foo> ptr) {}

int main() {
  std::unique_ptr<Foo> foo(new Foo{"sausage", 4});
  FuncCall(foo);
}

Or you can avoid pointers:

或者你可以避免指针:

void FuncCall(Foo foo) {}

int main() {
  FuncCall({sausage", 4});
}

#3


Be aware that the following:

请注意以下事项:

class Foo {
    std::string m_bar = "Bar";
    int m_baz = 3;
    float m_boo = 4.2;
    /* ... */
public:
    Foo() {}    // or Foo() = default or elision
};

int main() {
    Foo f;
    f.m_bar = "wunderBar";
}

Expands out to be along the lines of the following:

扩展为以下几行:

Foo* fptr = stack_allocate<Foo*>(sizeof(Foo));
// from ctor
fptr->m_bar.string("Bar"); // construct m_bar
fptr->m_baz.int(3);
fptr->m_boo.float(4.2);
// your code:
fptr->m_bar.operator=("wunderBar");

For similar reasons, you might want to look at the IL instructions for your C# construct - you'll find it's performing equally redundant operations (and in more complex situations, possibly boxing/unboxing).

出于类似的原因,您可能希望查看C#构造的IL指令 - 您会发现它执行同样冗余的操作(在更复杂的情况下,可能是装箱/拆箱)。

Your C++ approach will also fail you when you incorporate non-copyable or non-movable types which will force you to pass pointers and/or bend your design.

当您合并不可复制或不可移动的类型时,您的C ++方法也会失败,这将迫使您传递指针和/或弯曲您的设计。

What it /seems/ you are trying to do is recreate Python's optional parameters:

您/您正在尝试做的是重新创建Python的可选参数:

# C++-alike
class Foo(object):
    def __init__(self, Bar, Baz, Boo):
        ...

# C#-alike:
class Foo(object):
    def __init__(self, Bar="Bar", Baz=13, Boo=4.2):
        ...

C++ doesn't provide a direct way of doing this, the closest mechanisms are default parameters and operator overloading:

C ++没有提供直接的方法,最接近的机制是默认参数和运算符重载:

class Foo {
    std::string m_bar = "Bar";
    int m_baz = 3;
    float m_boo = 4.2;
public:
    Foo(std::string bar="Bar", int baz=3, int boo=6.1)
        : m_bar(bar), m_baz(baz), m_boo(boo)
        {}
    /* Foo* f = new Foo(); => new Foo(bar="Bar", baz=13, boo=6.1);
     * Foo* f = new Foo("hello"); => new Foo(bar="hello", baz=3, boo=4.2);
     * Foo* f = new Foo("hello", 1, 1.); => new Foo(bar="hello", baz=1, boo=1.);
     * Foo* f = new Foo(42.); => invalid, arguments must be in order.
     */    
};

or

class Foo {
    std::string m_bar = "Bar";
    int m_baz = 3;
    float m_boo = 4.2;
public:
    Foo() = default;

    // allow Foo("hello")
    Foo(const char* bar) : m_bar(bar) {}
    Foo(const std::string& bar) : m_bar(bar) {}
    Foo(std::string&& bar) : m_bar(std::forward(bar)) {}

    // allow Foo(10, 12.)
    explicit Foo(int baz, float boo) : m_baz(baz), m_boo(boo) {}

    /* Foo* f = new Foo(); => new Foo(bar="Bar", baz=3, boo=4.2);
     * Foo* f = new Foo("hello"); => new Foo(bar="hello", baz=3, boo=4.2);
     * Foo* f = new Foo(1, 1.); => new Foo(bar="Bar", baz=1, boo=1.);
     * Foo* f = new Foo(42.); => invalid, no match
     */    
};

See http://ideone.com/yFIqlA for SSCE.

有关SSCE,请参见http://ideone.com/yFIqlA。

If you genuinely have a dozen different constructor configurations, you should probably rethink your design.

如果你真的有十几个不同的构造函数配置,你应该重新考虑你的设计。

--- Edit ---

---编辑---

Note: It's not compulsory that you expose all parameters in the constructor:

注意:在构造函数中公开所有参数并非强制要求:

class Foo {
    std::string m_user_supplied;
    std::time_t m_time;
public:
    Foo() : m_user_supplied(), m_time(0) {}
    Foo(std::string src) : m_user_supplied(src), m_time(0) {}
    void addTime(time_t inc) { m_time += inc; }
};

--- Edit 2 ---

---编辑2 ---

"should maybe rethink your design" ... One problem with large, optional lists of parameters is growth. You are likely to wind up with parameters that depend on each other, contradict each other or interact with each other. You can either choose not to validate these or you can end up with complicated constructors.

“应该重新考虑你的设计”......大的,可选的参数列表的一个问题是增长。您可能会遇到彼此依赖,相互矛盾或相互影响的参数。您可以选择不验证这些,也可以使用复杂的构造函数。

struct Foo {
    ...
    FILE* m_output;
    const char* m_mode;
    ...

    Foo(..., FILE* output, const char* mode, ...)
    {
        ...
        if (output != nullptr) {
            ASSERT( output == nullptr || mode != nullptr );
            ... other requirements
        } else {
            if (mode != nullptr)
                ... might not be an error but it might be a bug ...
        }
        ...
    }
};

One approach to avoiding this is to use encapsulation/aggregation of related members.

避免这种情况的一种方法是使用相关成员的封装/聚合。

class Foo {
    ...
    struct FileAccess {
        FILE* m_file;
        const char* m_mode;
        constexpr FileAccess() : m_file(nullptr), m_mode(nullptr) noexcept {}
        FileAccess(FILE* file, const char* mode) : m_file(file), m_mode(mode) {
            if (file == nullptr || mode == nullptr)
                throw invalid_argument("file/mode cannot be null");
        }
    };
    ...

    FileAccess m_access;

    Foo(..., FileAccess access, ...);
};

This can go a fair way to reducing the bloat. If your API is stable, you can use it with initializer lists (if your API is not stable and you do change will bite you in the ass)

这可以通过合理的方式减少膨胀。如果你的API是稳定的,你可以将它与初始化列表一起使用(如果你的API不稳定而且你确实做了改变会咬你的屁股)

auto fooWithFile = make_unique<Foo>{..., /*access=*/{stdout, "w"}, ...};
auto fooWithout  = make_unique<Foo>{..., /*access=*/{}, ...};

If you subsequently decide to stop using ctors and switch to using setters, this will translate reasonably well, since you can have overloaded "set" which takes one of the various configuration structs:

如果您随后决定停止使用ctors并切换到使用setter,这将很好地转换,因为您可以重载“set”,它采用各种配置结构之一:

auto foo = make_unique<Foo>();
foo->set(FileAccess(stdout, "w"))
   ->set(Position(Right, -100, Top, 180))
   ->set(DimensionPercentage(80, 75));

vs

auto foo = make_unique<Foo>() { # pseudo based on if C++ had the C# syntax
    m_file = stdout;
    m_mode = "w";
    m_xPosition = -100;
    m_xPositionRel = Right;
    m_yPosition = -180;
    m_yPositionRel = Top;
    m_dimensionType = Percentage;
    m_xDimension = 80;
    m_yDimension = 75;
};

#4


You can use uniform initialization to initialize it.

您可以使用统一初始化来初始化它。

#1


You might use a lambda:

你可以使用lambda:

FuncCall( []{ Foo f; f.Bar = "sausage"; f.Boo = 4; return f; }() );

Live example

#2


Foo has to have a constructor or be an aggregate type:

Foo必须有一个构造函数或者是一个聚合类型:

class Foo {
  std::string bar;
  int boo;
public:
  Foo(std::string s, int i) : bar(std::move(s)), boo(i) {}
};

void FuncCall(std::unique_ptr<Foo> ptr) {}

int main() {
  FuncCall(make_unique<Foo>("sausage", 4));
}

struct Foo {
  std::string bar;
  int boo;
};

void FuncCall(std::unique_ptr<Foo> ptr) {}

int main() {
  std::unique_ptr<Foo> foo(new Foo{"sausage", 4});
  FuncCall(foo);
}

Or you can avoid pointers:

或者你可以避免指针:

void FuncCall(Foo foo) {}

int main() {
  FuncCall({sausage", 4});
}

#3


Be aware that the following:

请注意以下事项:

class Foo {
    std::string m_bar = "Bar";
    int m_baz = 3;
    float m_boo = 4.2;
    /* ... */
public:
    Foo() {}    // or Foo() = default or elision
};

int main() {
    Foo f;
    f.m_bar = "wunderBar";
}

Expands out to be along the lines of the following:

扩展为以下几行:

Foo* fptr = stack_allocate<Foo*>(sizeof(Foo));
// from ctor
fptr->m_bar.string("Bar"); // construct m_bar
fptr->m_baz.int(3);
fptr->m_boo.float(4.2);
// your code:
fptr->m_bar.operator=("wunderBar");

For similar reasons, you might want to look at the IL instructions for your C# construct - you'll find it's performing equally redundant operations (and in more complex situations, possibly boxing/unboxing).

出于类似的原因,您可能希望查看C#构造的IL指令 - 您会发现它执行同样冗余的操作(在更复杂的情况下,可能是装箱/拆箱)。

Your C++ approach will also fail you when you incorporate non-copyable or non-movable types which will force you to pass pointers and/or bend your design.

当您合并不可复制或不可移动的类型时,您的C ++方法也会失败,这将迫使您传递指针和/或弯曲您的设计。

What it /seems/ you are trying to do is recreate Python's optional parameters:

您/您正在尝试做的是重新创建Python的可选参数:

# C++-alike
class Foo(object):
    def __init__(self, Bar, Baz, Boo):
        ...

# C#-alike:
class Foo(object):
    def __init__(self, Bar="Bar", Baz=13, Boo=4.2):
        ...

C++ doesn't provide a direct way of doing this, the closest mechanisms are default parameters and operator overloading:

C ++没有提供直接的方法,最接近的机制是默认参数和运算符重载:

class Foo {
    std::string m_bar = "Bar";
    int m_baz = 3;
    float m_boo = 4.2;
public:
    Foo(std::string bar="Bar", int baz=3, int boo=6.1)
        : m_bar(bar), m_baz(baz), m_boo(boo)
        {}
    /* Foo* f = new Foo(); => new Foo(bar="Bar", baz=13, boo=6.1);
     * Foo* f = new Foo("hello"); => new Foo(bar="hello", baz=3, boo=4.2);
     * Foo* f = new Foo("hello", 1, 1.); => new Foo(bar="hello", baz=1, boo=1.);
     * Foo* f = new Foo(42.); => invalid, arguments must be in order.
     */    
};

or

class Foo {
    std::string m_bar = "Bar";
    int m_baz = 3;
    float m_boo = 4.2;
public:
    Foo() = default;

    // allow Foo("hello")
    Foo(const char* bar) : m_bar(bar) {}
    Foo(const std::string& bar) : m_bar(bar) {}
    Foo(std::string&& bar) : m_bar(std::forward(bar)) {}

    // allow Foo(10, 12.)
    explicit Foo(int baz, float boo) : m_baz(baz), m_boo(boo) {}

    /* Foo* f = new Foo(); => new Foo(bar="Bar", baz=3, boo=4.2);
     * Foo* f = new Foo("hello"); => new Foo(bar="hello", baz=3, boo=4.2);
     * Foo* f = new Foo(1, 1.); => new Foo(bar="Bar", baz=1, boo=1.);
     * Foo* f = new Foo(42.); => invalid, no match
     */    
};

See http://ideone.com/yFIqlA for SSCE.

有关SSCE,请参见http://ideone.com/yFIqlA。

If you genuinely have a dozen different constructor configurations, you should probably rethink your design.

如果你真的有十几个不同的构造函数配置,你应该重新考虑你的设计。

--- Edit ---

---编辑---

Note: It's not compulsory that you expose all parameters in the constructor:

注意:在构造函数中公开所有参数并非强制要求:

class Foo {
    std::string m_user_supplied;
    std::time_t m_time;
public:
    Foo() : m_user_supplied(), m_time(0) {}
    Foo(std::string src) : m_user_supplied(src), m_time(0) {}
    void addTime(time_t inc) { m_time += inc; }
};

--- Edit 2 ---

---编辑2 ---

"should maybe rethink your design" ... One problem with large, optional lists of parameters is growth. You are likely to wind up with parameters that depend on each other, contradict each other or interact with each other. You can either choose not to validate these or you can end up with complicated constructors.

“应该重新考虑你的设计”......大的,可选的参数列表的一个问题是增长。您可能会遇到彼此依赖,相互矛盾或相互影响的参数。您可以选择不验证这些,也可以使用复杂的构造函数。

struct Foo {
    ...
    FILE* m_output;
    const char* m_mode;
    ...

    Foo(..., FILE* output, const char* mode, ...)
    {
        ...
        if (output != nullptr) {
            ASSERT( output == nullptr || mode != nullptr );
            ... other requirements
        } else {
            if (mode != nullptr)
                ... might not be an error but it might be a bug ...
        }
        ...
    }
};

One approach to avoiding this is to use encapsulation/aggregation of related members.

避免这种情况的一种方法是使用相关成员的封装/聚合。

class Foo {
    ...
    struct FileAccess {
        FILE* m_file;
        const char* m_mode;
        constexpr FileAccess() : m_file(nullptr), m_mode(nullptr) noexcept {}
        FileAccess(FILE* file, const char* mode) : m_file(file), m_mode(mode) {
            if (file == nullptr || mode == nullptr)
                throw invalid_argument("file/mode cannot be null");
        }
    };
    ...

    FileAccess m_access;

    Foo(..., FileAccess access, ...);
};

This can go a fair way to reducing the bloat. If your API is stable, you can use it with initializer lists (if your API is not stable and you do change will bite you in the ass)

这可以通过合理的方式减少膨胀。如果你的API是稳定的,你可以将它与初始化列表一起使用(如果你的API不稳定而且你确实做了改变会咬你的屁股)

auto fooWithFile = make_unique<Foo>{..., /*access=*/{stdout, "w"}, ...};
auto fooWithout  = make_unique<Foo>{..., /*access=*/{}, ...};

If you subsequently decide to stop using ctors and switch to using setters, this will translate reasonably well, since you can have overloaded "set" which takes one of the various configuration structs:

如果您随后决定停止使用ctors并切换到使用setter,这将很好地转换,因为您可以重载“set”,它采用各种配置结构之一:

auto foo = make_unique<Foo>();
foo->set(FileAccess(stdout, "w"))
   ->set(Position(Right, -100, Top, 180))
   ->set(DimensionPercentage(80, 75));

vs

auto foo = make_unique<Foo>() { # pseudo based on if C++ had the C# syntax
    m_file = stdout;
    m_mode = "w";
    m_xPosition = -100;
    m_xPositionRel = Right;
    m_yPosition = -180;
    m_yPositionRel = Top;
    m_dimensionType = Percentage;
    m_xDimension = 80;
    m_yDimension = 75;
};

#4


You can use uniform initialization to initialize it.

您可以使用统一初始化来初始化它。