9

I have tried to use C++0x initializer list as argument to a constructor call in this way:

Foo<float> foo("Foo 1", std::vector<const char *>({ "foo A", "foo B" }) );

with the constructor

Foo(const char *name, std::vector<const char *> &foos)

With this constructor the compiler complained:

error: no matching function for call to Foo<float>::Foo(
    const char [5], std::vector<const char *, std::allocator<const char *> >)
note: candidates are: Foo<T>::Foo(const char *, std::vector<const char *,
    std::allocator<const char *> >&) [with T = float]

However, when I've changed the constructor to

Foo(const char *name, std::vector<const char *> foos)

Everything worked as expected. Why does the first constructor not work? I thought the vector could be constructed in the place of constructor call and passed down by reference, but obviously there's some problem. Could anybody explain that?

Thanks

Btw. I am using g++ version 4.4.5

EDIT: Thanks to the correct answers below, I have found also why I can't do that.

Community
  • 1
  • 1
Radim Vansa
  • 5,686
  • 2
  • 25
  • 40
  • 3
    Have you tried a const reference? – Oliver Charlesworth Jun 10 '11 at 10:19
  • Why are you saying `std::vector({ "foo A", "foo B" })` ? That's needlessly introducing another temporary using the move constructor that the compiler has to elide. Better say `std::vector{ "foo A", "foo B" }`. Of course you can just say `Foo foo{"Foo 1", {"foo A", "foo B"}}`. – Johannes Schaub - litb Jun 10 '11 at 10:56

6 Answers6

10

You cannot bind a temporary to a T&.

You can bind a temporary to T const&:

Foo(const char* name, std::vector<const char*> const& foos)

But I'd question the sanity of a vector of char pointers. What's wrong with std::string?

Lightness Races in Orbit
  • 378,754
  • 76
  • 643
  • 1,055
  • Says about everything that has gone wrong here. Could you also not just pass the brace initializer list without the explicit call to the `vector` constructor? – rubenvb Jun 10 '11 at 10:34
  • You're right, when I used const std::vector<> &, I can pass the brace initializer list itself. I am not using std::string, because the strings mostly get passed to C API underneath as they are - no concatenating and such. – Radim Vansa Jun 10 '11 at 10:56
  • 1
    Flavius: well, that's really no reason not to use `std::string`. It's safer (not ugly pointers all over the place) and `std::string::c_str()` only returns the address of the beginning of the internal data array (in all modern implementations, this is mandated by the C++11 Standard), which is instantaneous. – rubenvb Jun 10 '11 at 11:06
  • @Flavius: rubenvb is right. Needing to pass a char buffer to underlying C APIs is absolutely _not_ a reason to avoid `std::string`. (What you said would be rather like storing an `int` as its individual, component bits in `bool`s, just because you never do a `+` operation.) The benefits are enormous. – Lightness Races in Orbit Jun 10 '11 at 11:13
4

Temporary cannot be bound to non-const reference, so do this:

Foo(const char *name, const std::vector<const char *> &foos)
                    //^^^^ note this
Nawaz
  • 353,942
  • 115
  • 666
  • 851
2

The initializer list is a red hering, I think. You are trying to bind a temporary to a non-const reference, which is illegal. Try using a const reference.

Foo(const char *name, std::vector<const char *> const& foos)
tokage
  • 196
  • 3
1

std::vector<const char *> &foos is lvalue reference. You are trying to pass rvalue, this is wrong. You can use either rvalue reference std::vector<const char *> &&foos or const reference const std::vector<const char *> &foos

pure cuteness
  • 1,635
  • 11
  • 26
0

You cannot bind an rvalue std::vector to a non-const lvalue reference. You must take a const lvalue reference or an rvalue reference.

Puppy
  • 144,682
  • 38
  • 256
  • 465
0

You already have your answer, but since you have "initializer list" in the title, you may want to consider writing a (C++0x) initializer-list constructor, e.g. like so:

struct Foo {
    Foo(const char* name, std::initalizer_list<const char*> il)
      : name(name), buf(il)
    { }
private:
    const char * const name;
    std::vector<const char *> buf;
};

Then you're no longer an aggregate, but you can construct it like so:

Foo x("Name", { "ab", "cd", "ef" });

You even pass il as const-reference if you prefer.

Kerrek SB
  • 464,522
  • 92
  • 875
  • 1,084