Cannot convert Type in initialization
Asked Answered
A

1

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

Aerator answered 24/1, 2015 at 17:24 Comment(1)
related same reason for the errorYap
L
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)
    //                           ^^^^^^
{};
Limnology answered 24/1, 2015 at 17:34 Comment(6)
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?Triciatrick
@Triciatrick Boo{1,2} does perform aggregate initialization. Boo{anything, at, all} is going to be aggregate initialization because Boo is an aggregate.Limnology
@Triciatrick 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."Limnology
This is a defect in the standard that's been fixed by the resolution of CWG issue 1467.Dialyse
@Dialyse 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.Limnology
@Limnology seems to be fixed on gcc trunk but not clang. I don't know of any good way to track when compilers implement core issue resolutions, though.Dialyse

© 2022 - 2024 — McMap. All rights reserved.