Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

C++ automatic type deduction in constructor

Tags:

c++

c++14

I am trying to understand the following code that I saw today. I already tried to find a related question, but since I have no idea what this feature of C++ is called it is hard to find related posts. A hint on the correct search term might already help me.

struct A
{ int x; };

struct B
{ B(A a) {}; };

int main()
{
    B b{ { 5 } }; // works, seems to create a struct A from {5} and pass it to B's constructor
    std::make_unique<B>({ 5 }); // doesn't compile
    return 0;
}

Why is {5} not used to create a struct A when passed to make_unique but is used this way in the constructor of B?

If B had a second constructor B(int foo) {}; this one would be used instead of the one frome above (at least that is what I found by trial and error). What is the rule to decide if the argument is automatically used to create a struct A or if it is used directly as int in the constructor?

I am using Visual C++ 14.0

like image 545
AlbertM Avatar asked Aug 03 '26 20:08

AlbertM


1 Answers

Here's a simplified demonstration:

struct X { X(int); };

void foo(X );
template <typename T> void bar(T );

foo({0}); // ok
bar({0}); // error

The issue is that braced-init-lists, those constructs that are just floating {...}s, are strange beasts in C++. They don't have a type - what they mean must be inferred from how they're actually used. When we call foo({0}), the braced-init-list is used to construct an X because that's the argument - it behaves as if we wrote X{0}.

But in bar({0}), we don't have sufficient context to know what to do with that. We need to deduce T from the argument, but the argument doesn't have a type - so what type could we possibly deduce?

The way to make it work, in this context, is to explicitly provide that T:

bar<X>({0}); // ok

or provide an argument that has a type that can be deduced:

bar(X{0});   // ok

In your original example, you can provide the A directly:

make_unique<B>(A{5})

or the B directly:

make_unique<B>(B({5}))

or just use new:

unique_ptr<B>(new B({5}))

or, less preferred and somewhat questionable, explicitly specify the template parameter:

make_unique<B, A>({5});
like image 167
Barry Avatar answered Aug 06 '26 10:08

Barry