7

Consider following code:

#include <iostream>
#include <vector>

struct C {
    std::vector<int> a;
    std::string b;
    bool c;
};

void printC(const C &c) {
    // ...
}

int main() {
    printC({
        { 1, 2, 3 },
        "ehlo",
        false
    });
}

This works, because compiler can generate proper constructor for me. But if I change struct C to this:

struct C {
    std::vector<int> a;
    std::string b;
    bool c;

    C() {
        c = false;
    }
};

The printC call stops working because compiler stops generating appropriate constructor. I've tried to write myself a constructor using std::initializer_list but failed.

So the question is - How to write constructor that will make the above code compile and work again?

1
  • Don't forget to #include <string>! Commented Dec 20, 2018 at 11:37

3 Answers 3

6

I've tried to write myself a constructor using std::initializer_list but failed.

You don't need one. You just need a c'tor taking a vector, string and boolean:

C(std::vector<int> a, std::string b, bool c) 
  : a(std::move(a))
  , b(std::move(b))
  , c(c) {
}

Your code should now be well-formed again. Though now it incurs two move operations, while the original aggregate version could have initialized the elements of your object directly. It's something worth considering.

Sign up to request clarification or add additional context in comments.

3 Comments

Eh, that's gonna get inlined. If you the code is really really performance-sensitive, you might want to look at the generated assembly to confirm, but in general, it is not something I would consider.
Context where I came accross this problem fortunately doesn't care about performance much. This is the correct answer, I originally had struct S nested in unordered map and didn't realize that not everything is initializer list.
@doomista - You aren't gonna see a performance hit even with the moves. Vector and string are fairly optimized. But this applies to all user defined types, where a move can be arbitrarily expansive or cheap. It's something worth keeping in mind.
5

Worth noting that in C++14 and later you can just use a default member initializer:

struct C {
    std::vector<int> a;
    std::string b;
    bool c = false;
};

Also, aggregate initialization generates no constructors. It bypasses them entirely.

Comments

3

You can pass an instance of a std::initializer_list<int> like this:

#include <initializer_list>

struct C {
    /* Data members... */

    C(std::initializer_list<int> vecData, std::string str, bool flag) :
        a{vecData}, b{std::move(str)}, c{flag} {}
};

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.