Perfect forwarding problem

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
// base class
class resource
{
protected:
	std::string name;
public:
	template< typename TStr >
	resource(TStr&& n) : name(std::forward<TStr>(n)) {}

	resource(resource&& o) : name(std::move(o.name)) 
	{ 
	}
        ...
};

// child
class image : public resource
{
private:
	int* img_data;
public:
	template< typename TStr >
	image(TStr&& n) : resource(std::forward<TStr>(n)), img_data(new int[500]) {}

	image(image&& o) : resource(std::move(o)), img_data(o.img_data)
	{
		o.img_data = nullptr;
	}
        ...
};

When the call is made to image move-copy-ctor, calling resource(std::move(o)) don't want bind to resource move-copy-ctor but instead to ctor that excepts string.
How to resolve this?

Thanks for your time.
So instead of calling the resource move - ctor you want it to call resource string constructor?
image move-copy-ctor should call resource move-copy-ctor.
I dont want it to bind to resource ctor that excepts string.
Last edited on
Ok, so it is not making the call to the move-copy-ctor is what you are saying? Can you show the call site? (Because it sure looks good)
Last edited on

Ok, so it is not making the call to the move-copy-ctor is what you are saying?

Yes.

Its compile time error.

Error output:

...error C2664: 'std::basic_string<_Elem,_Traits,_Alloc>::basic_string(const std::basic_string<_Elem,_Traits,_Alloc> &)' : cannot convert parameter 1 from 'image' to 'const std::basic_string<_Elem,_Traits,_Alloc> &'


and:

...see reference to function template instantiation 'resource::resource<image>(TStr &&)' being compiled

witch points to line 25 in above code.
Last edited on
image(image&& o) : resource(std::move(o.name)), img_data(o.img_data) Try this?

It's complaining about std::move(o) which is an image and not a std::string
Last edited on
That will do the wrong thing! I do want it to call resource move-copy-ctor to be able to move data. This code is just short example of what i had in mind. resource ctor that expects string actually allocates some other data.
Whoops your right, I just looked at the error and went from there, checking one of my projects brb.
No problem, its my fault. My english sucks so i can't explain correctly what i want.
The problem is that when the template is being deduced it sees the
1
2
template< typename TStr >
	resource(TStr&& n) : name(std::forward<TStr>(n)) {}
as if it takes an image, and it cannot implicitly or explicitly conver n to a std::string. To make your code work I just implemented a move-ctor that takes a string object. But that is not what you want, so instead implement the move-ctor for the resource itself..
1
2
3
resource(resource&& o) : name(std::move(o.name)) //...
	{ 
	}
I am idiot!!!
1
2
//template< typename TStr >
resource(std::string&& n) : name(std::forward<std::string>(n)) {}


And use perfect forwarding only for functions/methods with multiple parameters.
image&& => resource&& requires a conversion;
the templated constructor requires none and the overload resolves to that.

1
2
3
4
5
6
7
8
9
10
11
12
13
class image : public resource
{
        // ...

	//image(image&& o) : resource(std::move(o)), img_data(o.img_data)
        // a cast from image&& to resource&& is required here to resolve to the base class move constructor  
        image(image&& o) : resource( static_cast<resource&&>(o) ), img_data(o.img_data)
	{
		o.img_data = nullptr;
	}

        // ...
};
According to Scott Meyers it does not need a cast.
http://skillsmatter.com/podcast/home/move-semanticsperfect-forwarding-and-rvalue-references
somwhere in the end of video it shows somewhat same code as this.

EDIT: look at 55:10
Last edited on
> According to Scott Meyers it does not need a cast.

First ask Scott Meyers to add a templated constructor to the base class.
And then ask him once again about not needing a cast.

Why don't you realize that the problem that you are facing has got nothing specific to do with r-value references, move semantics or perfect forwarding. They are just irrelevant, incidental distractions to the real issue - resolving a constructor invocation between a templated constructor and a non-templated constructor.

Compile and run this program (which has nothing that C++98 does not have):
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
#include <iostream>

struct base
{
   template< typename T > base( T ) {}
   base( const base& ) { std::cout << "base::copy_constructor\n" ; }
};

struct derived : base
{
    derived( int i ) : base(i) {}

    derived( const derived& d ) : base(d) 
    { std::cout << "derived::copy_constructor\n" ; }
};

int main()
{
    derived one(1) ;
    derived two(one) ;
}


Now modify it, compile and run it :
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
#include <iostream>

struct base
{
   template< typename T > base( T ) {}
   base( const base& ) { std::cout << "base::copy_constructor\n" ; }
};

struct derived : base
{
    derived( int i ) : base(i) {}

    derived( const derived& d ) : base( static_cast<const base&>(d) )
    { std::cout << "derived::copy_constructor\n" ; }
};

int main()
{
    derived one(1) ;
    derived two(one) ;
}


And thou shalt be enlightened.
Topic archived. No new replies allowed.