Forwarding all constructors in C++0x
There is a better way in C++0x for this
class X: public Super {
using Super::Super;
};
If you declare a perfect-forwarding template, your type will behave badly in overload resolution. Imagine your base class is convertible from int
and there exist two functions to print out classes
class Base {
public:
Base(int n);
};
class Specific: public Base {
public:
template<typename... Args>
Specific(Args&&... args);
};
void printOut(Specific const& b);
void printOut(std::string const& s);
You call it with
printOut("hello");
What will be called? It's ambiguous, because Specific
can convert any argument, including character arrays. It does so without regard of existing base class constructors. Inheriting constructors with using declarations only declare the constructors that are needed to make this work.
C++0x perfect forwarding getting in the way of copy ctor?
Yes, this is the correct behaviour.
There are two constructors that might be called in this case - your copy constructor and the template one. However, template constructor might deduce the type to be perfect match (Wrapper(StringT&&)
with StringT = Wrapper&
yields perfect match Wrapper(Wrapper&)
) and thus gets used instead of copy constructor.
Suggested workaround - use std::enable_if
from <type_traits>
:
template <typename StringT>
Wrapper(StringT&& value,
typename std::enable_if<
!std::is_same<
StringT,
Wrapper&
>::value
>::type* = 0)
: value(std::forward<StringT>(value))
{ }
See it working here.
How useful would Inheriting Constructors be in C++?
2) Are there any technical reasons you can think of that would preclude "perfect forwarding constructors" from being an adequate alternative?
I have shown one problem with that perfect forwarding approach here: Forwarding all constructors in C++0x .
Also, the perfect forwarding approach can't "forward" the expliciteness of base-class constructors: Either it is always a converting constructor or never, and the base-class will always be direct initialized (always making use of all constructors, even explicit ones).
Another problem are initializer-list constructors because you can't deduce Args
to initializer_list<U>
. Instead, you would need to forward to the base with B{args...}
(note the braces) and initialize D
objects with (a, b, c)
or {1, 2, 3}
or = {1, 2, 3}
. In that case, Args
would be the element types of the initializer list, and forward them to the base class. A initializer-list constructor can then receive them. This seems to cause unnecessary code bloat because the template argument pack will potentially contain lots of type sequences for each different combination of types and length and because you have to choose an initialization syntax this means:
struct MyList {
// initializes by initializer list
MyList(std::initializer_list<Data> list);
// initializes with size copies of def
MyList(std::size_t size, Data def = Data());
};
MyList m{3, 1}; // data: [3, 1]
MyList m(3, 1); // data: [1, 1, 1]
// either you use { args ... } and support initializer lists or
// you use (args...) and won't
struct MyDerivedList : MyList {
template<class ... Args>
MyDerivedList(Args&& ... args) : MyList{ args... } { }
};
MyDerivedList m{3, 1}; // data: [3, 1]
MyDerivedList m(3, 1); // data: [3, 1] (!!)
Passing/Moving parameters of a constructor in C++0x
You take each one by value, like this:
struct foo
{
foo(std::string s, bar b, qux q) :
mS(std::move(s)),
mB(std::move(b)),
mQ(std::move(q))
{}
std::string mS;
bar mB;
qux mQ;
};
The initialization of the function parameters by the argument will either be a copy-constructor or move-constructor. From there, you just move the function parameter values into your member variables.
Remember: copy- and move-semantics are a service provided by the class, not by you. In C++0x, you no longer need to worry about how to get your own "copy" of the data; just ask for it and let the class do it:
foo f("temporary string is never copied", bar(), quz()); // no copies, only moves
foo ff(f.mS, f.mB, f.mQ); // copies needed, will copy
foo fff("another temp", f.mB, f.mQ); // move string, copy others
Note: your constructor only takes in values, those values will figure out how to construct themselves. From there, of course, it's up to you to move them where you want them.
This applies everywhere. Have a function that needs a copy? Make it in the parameter list:
void mutates_copy(std::string s)
{
s[0] = 'A'; // modify copy
}
mutates_copy("no copies, only moves!");
std::string myValue = "don't modify me";
mutates_copy(myValue); // makes copy as needed
mutates_copy(std::move(myValue)); // move it, i'm done with it
In C++03, you could emulate it fairly well, but it wasn't common (in my experience):
struct foo
{
foo(std::string s, bar b, qux q)
// have to pay for default construction
{
using std::swap; // swaps should be cheap in any sane program
swap(s, mS); // this is effectively what
swap(b, mB); // move-constructors do now,
swap(q, mQ); // so a reasonable emulation
}
std::string mS;
bar mB;
qux mQ;
};
c++0x inherited constructor in templates
template <typename T>
struct bar: public foo<T>
{
using foo<T>::foo<T>;
};
To let this parse correctly, you would need to insert template
before the foo<T>;
, to tell the compiler that foo
is to be regarded as a template name (it cannot look into foo<T>
to tell itself, since T
is unknown). But using ::template
is not allowed in a using declaration. The name also does not refer to all constructors of bar
: Instead, it would refer to a specific constructor function template specialization (T
is the template argument) of such a constructor as follows
template<typename T>
foo();
In addition, it's not valid for a using declaration to use a template-id
(like foo<T>
) as its name (which in effect forbids it to refer to function template specialization, with the addition of forbidding to name conversion function template specializations stated too), so even if you correct the parsing problem using ::template
(if it would be possible), you would still error out at this point.
When inherited constructors were introduced, special rules were added that allow to reference a constructor using a syntactic rule: If you have a qualified-id (which basically a qualified name using ...::...
), and the last qualified before the final part names a specific class, then you can denote the constructor(s) of that class in two additional ways:
- If the class was named using a template-id (a name of the form
foo<T>
) and the final part matches the template name (so,foo<T>::foo
orTTP<T>::TTP
withTTP
being a template template parameter). - If the final part matches the class name (so,
foo::foo
orT::T
, withT
being a template parameter).
These two additional rules are only active in a using declaration. And they were naturally not present in C++03. The other rule that was also present in C++03 is: If the final part names the injected class name, then this qualified name also refers to the constructor:
foo::foo
would therefor work. But with this rule alone,T::T
(whereT
denotes classfoo
) would not work, becausefoo
has no member calledT
.
Therefor, with the special rules in place you can write
using foo<T>::foo;
using bar::foo::foo; // valid too
The second is valid too: foo
is the injected class name which was injected into the base class foo<T>
and inherited to bar
. We refer to that name by bar::foo
, and then add the last part foo
, which refers to the injected class name again, to denote the constructor(s) of `foo.
Now you understand why the initial name you tried would refer to a constructor function template specialization (if it were to be allowed to): Because the foo<T>::foo
part would name all constructors, and the <T>
that would follow would then filter out the template and pass the type argument.
Why is forwarding reference constructor called instead of copy constructor?
I am guessing that it's because the move constructor was implicitly defined but not the copy constructor.
No, both are implicitly defined for class Something
.
Why is the copy constructor being resolved to the templated forwarding reference constructor
Because the copy constructor takes const Something&
as its parameter. That means for the copy constructor to be called, implicit conversion is needed for adding const
qualifier. But the forwarding reference constructor could be instantiated to take Something&
as its parameter, then it's an exact match and wins in the overload resolution.
So if you make something
const
, the implicitly defined copy constructor will be invoked for the 3rd case instead of the forwarding reference constructor.
LIVE
but not the move constructor?
Because for the move constructor the above issue doesn't matter. For the invocation of the 1st and 2nd case, both implicitly defined move constructor and forwarding reference constructor are exact match, then non-template move constructor wins.
rvalue references and constructor arguments
On the contrary, C++11 makes it easier thanks to universal references:
template <typename T> struct Wrapper
{
T value;
template <typename U> Wrapper(U && u)
: value(std::forward<U>(u))
{ }
};
As an extra nice touch, you should add a defaulted second argument that only exists when T
is constructible from U
, so as to not make your class itself appear constructible from unmatching types. And make it variadic, too:
template <typename ...Args>
Wrapper(Args &&... args,
typename std::enable_if<std::is_constructible<T, Args...>::value, int>::type = 0)
: value(std::forward<Args>(args)...)
{ }
Make sure to #include <utility>
for forward
and #include <type_traits>
for the traits.
Related Topics
How to Building Static Qt with Static Openssl
C++ Overloading Array Operator
Double Delete in Initializer_List VS 2013
Why Is a Constexpr Function on a Reference Not Constexpr
What Is the Proper Function for Comparing Two C-Style Strings
Errors When Linking to Protobuf 3 on Ms Visual C
How to Find a Search Term in Source Code
Static Member Access in Constant Expressions
Why Does C++ Allow Variable Length Arrays That Aren't Dynamically Allocated
At What Point Does Dereferencing the Null Pointer Become Undefined Behavior
Will Consteval Functions Allow Template Parameters Dependent on Function Arguments
Defining a Variable in the Condition Part of an If-Statement
Is There Any Way a C/C++ Program Can Crash Before Main()