5

I think I miss something and I don't know what exactly. Let's take a look at code snippet.

template <typename T>
struct Foo { 
    Foo (int n, int p, string s, T t = {})
    : m_n {n}, m_p {p}, m_s {s}, m_t {t}
    {}

    const int m_n;
    const int m_p;
    const string m_s;
    T m_t;
};

And usage looks like this:

Foo<int> f_int1 {1, 2, "A", 155};
Foo<int> f_int2 {1, 2, "A"};

Everything is like intended. But when I want to have a user defined type as a T parameter of Foo, some errors occur. Consider:

struct Boo {
    int z;
    int l;
};

And usage:

Foo<Boo> f_boo1 {1, 2, "A"};
Foo<Boo> f_boo2 {1, 2, "A", {1, 2}};

These both instructions give (gcc 4.8.1):

cannot convert ‘Boo’ to ‘int’ in initialization

I can create Boo objects like this:

Boo boo1 {};
Boo boo2 {1, 2};

So, could you tell me where the problem is?

Possible solution:

struct Boo {
    Boo () : z {}, l {} {}
    Boo (int p1, int p2) : z {p1}, l {p2} {}

    int z;
    int l;
};

And both below instructions work as intended:

Foo<Boo> f_boo1 {1, 2, "A"};
Foo<Boo> f_boo2 {1, 2, "A", {1, 2}};

For me, that's ok, I don't see any reason why not to add two constructors to the class, but what if the type is not mine? Should I write simple wrapper with the constructors?

Thanks, Artur

1
  • related same reason for the error Commented Jan 24, 2015 at 17:38

1 Answer 1

7

That's because you're trying to perform aggregate initialization on Boo. See §8.5.4/3:

List-initialization of an object or reference of type T is defined as follows:

— If T is an aggregate, aggregate initialization is performed (8.5.1).

You're intending to copy-construct your Boo... but really you're doing aggregate initialization, which leads to trying to construct int z from a Boo, hence the error

error: no viable conversion from 'Boo' to 'int'

Note that you could reproduce your problem in much less code and without any templates:

Boo b;
Boo b2{b}; // error

The fix is simple. Just don't use list-initialization:

template <typename T>
struct Foo { 
    Foo (int n, int p, string s, T t = {})
    : m_n {n}, m_p {p}, m_s {s}, m_t(t)
    //                           ^^^^^^
{};
Sign up to request clarification or add additional context in comments.

6 Comments

can you just clarify a bit why {1,2} is implicitly converted to a Boo instead of being passed to the ctor using aggregate initialization? Isn't Foo<T> an aggregate?
@vsoftco Boo{1,2} does perform aggregate initialization. Boo{anything, at, all} is going to be aggregate initialization because Boo is an aggregate.
@vsoftco Foo<T> is not an aggregate because it has a user-provided constructor. Full definition (8.5.1): "An aggregate is an array or a class with no user-provided constructors, no private or protected non-static data members, no base classes, and no virtual functions."
This is a defect in the standard that's been fixed by the resolution of CWG issue 1467.
@T.C. Good! Do you know how to track when compilers update for these sorts of things? clang 3.5.0 and gcc 4.9.2 with -std=c++14 both still error (what-used-to-be-correctly?) for this.
|

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.