I'm in the process of writing a basic class template. It takes two argument types for its parameters. The idea of the class is to take one type in as a const ref
and the other as a ref
. The functionality of the class is to convert type A
to type B
where the object being created will end up being b
. I would like to have perfect-forwarding
or move semantics
a valid part of this class template.
For now here is my current class with just basic types, but plan to expand this to any 2 types with the use of a variadic construction.
#ifndef CONVERTER_H
#define CONVERTER_H
#include <utility>
template<class From, class To>
class Converter {
private:
From in_;
To out_;
public:
// Would like for From in to be a const (non modifiable) object
// passed in by perfect forwarding or move semantics and for
// To out to be returned by reference with perfect forwarding
// or move semantics. Possible Constructor Declarations - Definitions
// Using std::move
Converter( From&& in, To&& out ) :
in_{ std::move( in ) },
out_{ std::move( out ) }
{
// Code to convert in to out
}
// Or using std::forward
Converter( From&& in, To&& out ) :
in_{ std::forward<From>( in ) },
out_{ std::forward<To>( out ) } {
// Code to convert in to out.
}
// Pseudo operator()...
To operator()() {
return out_;
}
};
#endif // !CONVERTER_H
In either way that I declare the constructor(s) above with std::move
or std::forward
this class compiles on its own. Now when I include this and try to instantiate an object above calling its constructor(s)... if I do this:
int i = 10;
float f = 0;
Converter<int, float> converter( i, f );
This gives me a compiler error in Visual Studio 2017 in both cases.
1>------ Build started: Project: ExceptionManager, Configuration: Debug Win32 ------
1>main.cpp
1>c:\users\skilz80\documents\visual studio 2017\projects\exceptionmanager\exceptionmanager\main.cpp(54): error C2664: 'Converter<unsigned int,float>::Converter(Converter<unsigned int,float> &&)': cannot convert argument 1 from 'unsigned int' to 'unsigned int &&'
1>c:\users\skilz80\documents\visual studio 2017\projects\exceptionmanager\exceptionmanager\main.cpp(54): note: You cannot bind an lvalue to an rvalue reference
1>Done building project "ExceptionManager.vcxproj" -- FAILED.
========== Build: 0 succeeded, 1 failed, 0 up-to-date, 0 skipped ==========
Which is understandable enough {can't bind lvaule to rvalue ref
}.
However if I try to use the constructor(s) like this:
int i = 10;
float f = 0;
Converter<int,float> converter( std::move( i ), std::move( f ) );
// Or
Converter<int,float> converter( std::forward<int>( i ), std::forward<float>( f ) );
It compiles and builds regardless if std::move(...)
or std::forward<T>(...)
is used within the class.
To my better understanding it apparently seems that
std::move(...)
&std::forward<T>(...)
are nearly interchangeable and do the same thing except thatstd::forward<T>(...)
has the extra cast involved.
Right now as this class stands since I'm only showing basic types it would seem more plausible to use std::move
, however I may eventually want to use more complex types so I would like to design this ahead of time with that thought in mind, so I'm leaning towards std::forward<T>
for the perfect forwarding.
With this being said and to complete this class there are 3 questions coupled together.
- If I'm using either
std::move
orstd::forward
in the class's constructor's member initialize list, why would I have to use them again when instantiating the template class object; wouldn't this be considered redundant? If so how would the constructor look so that the user wouldn't have to usestd::move()
orstd::forward<T>()
when calling this constructor?- What would be the most general and type safe way to convert
A
toB
within this context?- Once the above two are answered with clarity then this last part within this context with the above mentioned standard classes or other similar types would then be what in regards to implementing the
operator()()
and how would it look with respect to what has already been mentioned above?
To finish things up concerning the 3 coupled questions above my final thought here is that at one point in time of the design process I had thought about using std::any
and its related functions as a possible part of its implementation process. I don't know if std::any
could be used in this context or not and if so how?
EDIT
Here might be a couple of plausible possible future uses of this class:
vector<int> vecFrom{1,2,3, ...};
set<int> setTo;
Converter<vector<int>, set<int>> converter( vecFrom, setTo );
Or after expanding...
vector<int> vecIntFrom{1,2,3, ...};
vector<string> vecStringFrom{ "a", "b", "c", ... };
map<int,string> mapTo;
Converter<vector<int>, vector<string>, map<int,string> converter( vecIntFrom, vecStringFrom, mapTo );
T&&
is a forwarding-reference only if template type deduction is in play. From your codeConverter<int, float> converter(i, f)
, there's no type deduction happening, so the two parameters end up being rvalue-references. – Garderobenot having
to use or callstd::move
,std::forward
when calling the constructor in general. – Deirdrastd::move
is the better option for this case. The purpose ofstd::forward
is to be used with a deduced template parameter argument – Doucettemplate parameter arguments
thenforward
is overkill andmove
is suitable... I think I'm starting to get the general concept of the overall differences between the two. – Deirdrafunction template
but decided to go with aclass template
instead for future reasons. I might want to add functionality say for reporting, statistical purposes, etc. ... Yes as this class is currently using basic types I do understand what it is that you are saying. However, lets say that later I have avector<int>{1,2,3,...}
as theFrom
and want theTo
to beset<int>{1,2,3...}
etc. ...? – Deirdrastd::transform
? – Brachiatepublic:
keyword in the class declaration. Thanks to user: JiveDadson for pointing it out. I do have them public in my IDE. – Deirdrareference
of theobjects
passed in so that the class itself can reference them for other in class member functions. – Deirdrato_
it was supposed to beout_
. Thank you for pointing that out. I was in the process of changing some of the variable names - identifiers while asking - writing this question. I was trying to have the code make clear sense of what was trying to be achieved. 90% of the time I will copy code right from my IDE to here, but every now & then I'll just type it out when then a classes are very simple or small. – Deirdraoperator()()
was returningto_
its supposed to beout_
. When originally asking - writing this question I did decide to change a few of the variable names to make the reading of it a little clearer. Originally in my code for the member variables I did haveFrom from_
andTo to_
same thing as in the constructor,From from
&To to
which is okay, but when reading it from a verbal sense it sounded redundant in my mind so I changed them to what they now are. My apologies for any inconvenience. – Deirdra