In the C++ 20 Standard the constructor of the class template std::reference_wrapper is a template.
template<class U>
constexpr reference_wrapper(U&&) noexcept(see below );
while the assignment operator is not a template
constexpr reference_wrapper& operator=(const reference_wrapper& x) noexcept;
What is the reason that this difference (template and non-template) between these special member functions exists?
On the other hand, I tried the following progarm using Visual C++ 2019.
#include <iostream>
#include <functional>
struct A
{
void f() const { std::cout << "A::f()\n"; }
virtual void g() const { std::cout << "A::g()\n"; }
};
struct B : A
{
void f() const { std::cout << "B::f()\n"; }
void g() const override { std::cout << "B::g()\n"; }
};
int main()
{
B b;
std::reference_wrapper<A> r( b );
r.get().f();
r.get().g();
r = std::reference_wrapper<B>( b );
}
and the compiler did not issue an error message relative to the assignment operator.
Is it a bug of the compiler or have I missed something?
If std::reference_wrapper<T> had a single constructor accepting T&, then it would lead to bugs like std::reference_wrapper<const T> being able to bind to temporaries of type T. So, originally there were two constructors (other than the copy constructor): one taking T&, and another taking T&&, which was defined as deleted, ensuring that a compile error would occur.
However, it was pointed out that this is not really what we want: it would be better if std::reference_wrapper<T> would have no constructor at all that accepts an rvalue of type T, as opposed to a deleted constructor. This is LWG 2993. (You'll notice that this issue is mentioned at the bottom of the cppreference page.) Thus, the constructor was changed to a template that is SFINAE-disabled as appropriate.
Once these issues are solved for the constructor, the assignment operator only needs to have a single overload taking reference_wrapper. The conversion issues will be handled by the constructor logic when the compiler forms the implicit conversion sequence.
Related
Implementing a copy constructor deletes the default move constructor in C++.
Only compiler generated copy and move constructors are trivial.
Created a templated conversion constructor from any type to the current type.
#include <format>
#include <iostream>
#include <type_traits>
template <typename T = int>
class Element {
public:
T value;
Element(const T value_) noexcept : value(value_) {};
// Here is the conversion constructor
template <typename TT>
Element(const Element<TT> &element) noexcept : value(element.value) {
std::cout << std::format(
"Element<{}>(const {}& {})\n",
typeid(T).name(), typeid(TT).name(), element.value
);
}
// uncommenting this breaks the assertions coming next
// Element(const Element &element) noexcept : value(element.value) {};
};
static_assert(std::is_trivially_move_constructible<Element<>>::value);
static_assert(std::is_trivially_copy_constructible<Element<>>::value);
// how it behaves
void foo_int(Element<int> element) {
std::cout << std::format("foo_int: {}\n", element.value);
}
void foo_double(Element<double> element) {
std::cout << std::format("foo_double: {}\n", element.value);
}
int main() {
Element<int> int_element {1};
Element<double> double_element {1.5};
foo_int(int_element);
foo_double(int_element);
// uncommenting doesn't compile - narrowing conversion
// foo_int(double_element);
foo_double(double_element);
return 0;
}
Demo in Compiler Explorer
Can someone explain to me why this conversion constructor is not matched with T == TT. In that case only the copy / move constructor is called.
Then I thought maybe I can call it manually with auto a = Element<int>::Element<int>(int_element);. But that gives an error: "obsolete declaration style".
It seems to be treated like a normal constructor, and only considered after the other special member functions.
Can someone explain me the rules or where I can read more about this behavior?
When type T is the same as TT, then you are making a copy. The standard explicitly states that the copy constructor is not a template:
non-template constructor for class X is a copy constructor if its
first parameter is of type X&, const X&, volatile X& or const volatile
X&, and either there are no other parameters or else all other
parameters have default arguments ([dcl.fct.default]).
https://eel.is/c++draft/class.copy.ctor#1
Also, in C++, a non-template is always preferred over a template if it's an exact match. Given there is a real copy constructor, and your templated conversion constructor, in copying contexts the copy constructor would be picked. The same thing would happen for ordinary functions too:
template <typename T>
void foo(T);
void foo(int);
foo(123); // will call the non-template version (since it's an exact match)
So I have a couple of functions, which I get a magic function object, that provides overload resolution on:
void foo1();
void foo2(int);
auto foo_ptr = boost::hana::overload(foo1,foo2);
//Later
foo_ptr(12); //Both Valid (Yeah!)
foo_ptr();
But the problem arises when I do something like this:
using ptr_t = decltype(foo_ptr);
struct mine
{
ptr_t ptr;
mine(ptr_t ptr) : ptr(ptr){}
};
mine m(foo_ptr);
When I try to compile this code I get error: no matching function for call to 'boost::hana::overload_t<void (*)(int)>::overload_t()'.
See for yourself on godbolt.....
Now my question is this:
Am I allowed to copy these overload objects around (the documentation for hana does not say one way or the other), and if so why does it fail when I put it in a class as a member?
This is arguably a bug in Boost.Hana. The constructor of overload_t is:
template <typename F_, typename ...G_>
constexpr explicit overload_t(F_&& f, G_&& ...g)
: overload_t<F>::type(static_cast<F_&&>(f))
, overload_t<G...>::type(static_cast<G_&&>(g)...)
{ }
Notice that these are unconstrained forwarding references. This is the common problem of having a forwarding reference constructor be a better match than the copy constructor, when you have a non-constant object. Reduced example:
struct X {
X();
template <typename F> X(F&&);
X(X const&) = default;
};
X x1;
X x2(x1); // does *not* call the copy constructor
However, if the object were const, then the copy constructor would be a better match. So the short fix is to just do that:
struct mine
{
ptr_t ptr;
mine(ptr_t const& ptr) : ptr(ptr){}
};
And now it compiles. Likewise, as Yakk suggests, move instead of copy would also work for the same reason:
struct mine
{
ptr_t ptr;
mine(ptr_t ptr) : ptr(std::move(ptr)) {}
};
I have started leaning C++ recently. And I have a question on std::forward with lvalue reference and rvalue reference.
In my understanding, The Func(mc) in the following code suppose to call
Func(T& t) because of template parameter deduction rule. And copy constructor of MyClass should be called inside the function with a message, "copy constructor". However I cannot get it when I run the program.
I checked that std::forwad with rvalue reference and copy constructor is working well in other lines.
Please help me to understand what happen in the code. If I make easy mistake or misunderstanding, I am sorry for taking your time. Thank you very much.
class MyClass {
public:
MyClass() { printf("constructor.\n"); };
MyClass(MyClass&) { printf("copy constructor.\n"); };
MyClass(const MyClass&) { printf("const copy constructor.\n"); };
MyClass(MyClass&&) { printf("move constructor.\n"); };
int val = 3;
};
template <typename T>
void Func(T&& t) {
T new_t_(std::forward<T>(t));
new_t_.val *= 2;
};
main() {
MyClass mc;
Func(mc); // lvalue <- Func(T&)
Func(MyClass()); // rsvalue <- Func(T&&)
printf("mc.val=%d\n", mc.val); // this is for check
MyClass mc2(mc); // this is for check of copy constructor
}
Output when I run program is following,
constructor.
constructor.
move constructor.
mc.val=6
copy constructor.
I think it should have "copy constructor" between the first and the second "constructor" messages.
Thank you very much again.
Func(mc);
This call will deduce T to be MyClass&. Note the reference. This is due to the way perfect forwarding has been introduced into C++: A forwarding reference such as the T&& function parameter of Func will be deduced to be an lvalue reference if the corresponding function argument is an lvalue-expression; otherwise (for xvalues and prvalues) it will not be deduced to be a reference type.
The type deduced for T will then be reference-collapsed with the && from the function parameter T&& t to form the final function parameter type. In the case Func(mc), T == MyClass&, so the function parameter becomes MyClass& &&, collapsed to MyClass&.
Since T == MyClass&, the declaration of the local variable new_t_ will declare a reference in this case. No new object will be created:
template <>
void Func<MyClass&>(MyClass& t) {
MyClass& new_t_(std::forward<MyClass&>(t));
new_t_.val *= 2;
}
Func(MyClass());
Here, the function argument is the prvalue-expression MyClass(). T will be deduced to MyClass (without reference). The function template instantiation produced looks like this:
template <>
void Func<MyClass>(MyClass&& t) {
MyClass new_t_(std::forward<MyClass>(t));
new_t_.val *= 2;
}
If you want to copy/move the function argument into a local variable, the std::decay metafunction can be used - or, more specific to the OP's problem, the std::remove_reference metafunction. E.g.
template <typename T>
void Func(T&& t) {
using DT = typename std::decay<T>::type;
DT new_t_(std::forward<T>(t));
new_t_.val *= 2;
}
I'm trying to understand std::is_convertible in C++11. According to cppreference.com, std::is_convertible<T,U>::value should evaluate to 1 iff "If an imaginary rvalue of type T can be used in the return statement of a function returning U". The wording says nothing about where that function might be declared, though. What should one expect when the copy constructor of U is private? What should one expect when T is an lvalue reference?
E.g., consider this code:
#include <iostream>
#include <type_traits>
struct Fact_A;
struct A {
friend struct Fact_A;
A() = default;
A(A&&) = delete;
private:
A(const A&) = default;
};
struct Ref_A {
A* _ptr;
Ref_A(A* ptr) : _ptr(ptr) {}
operator A& () { return *_ptr; }
};
struct Fact_A {
static A* make_A(const A& a) { return new A(a); }
static A f(A* a_ptr) { return Ref_A(a_ptr); }
//static A g(A&& a) { return std::move(a); }
};
int main() {
A a1;
A* a2_ptr = Fact_A::make_A(a1);
(void)a2_ptr;
std::cout << std::is_convertible< Ref_A, A >::value << "\n" // => 0
<< std::is_convertible< Ref_A, A& >::value << "\n" // => 1
<< std::is_convertible< A&, A >::value << "\n"; // => 0
}
I'm using gcc-4.8.2 or clang-3.4 (no difference in output), and I compile with:
{g++|clang++} -std=c++11 -Wall -Wextra eg.cpp -o eg
Here, std::is_convertible< Ref_A, A > reports 0. However, you can see that Fact_A::f returns an object of type A, and an rvalue of type Ref_A is used in its return statement. The problem is that the copy constructor of A is private, so that function cannot be placed anywhere else. Is the current behaviour correct with respect to the standard?
Second question. If I remove private, the output turns into 1 1 1. What does the last 1 mean? What is an "rvalue of type A&"? Is that an rvalue reference? Because you might notice I explicitly deleted the move constructor of A. As a result of this, I cannot declare Fact_A::g. But still, std::is_convertible< A&, A > reports 1.
is_convertible is defined as follows in [meta.rel]/4 from n3485:
Given the following function prototype:
template <class T> typename
add_rvalue_reference<T>::type create();
the predicate condition for a template specialization is_convertible<From, To>
shall be satisfied if and only if the return expression in the following code would be
well-formed, including any implicit conversions to the return type of
the function:
To test() {
return create<From>();
}
and here, you need a movable/copyable To: The return-statement applies an implicit conversion, and this requires an accessible copy/move constructor if To is a class type (T& is not a class type).
Compare to [conv]/3
An expression e can be implicitly converted to a type T if and only if the declaration T t=e; is well-formed, for some invented temporary variable t.
If From is T&, you get something like
To test() {
return create<T&>();
}
which, similar to std::declval, is an lvalue: The expression create<T&>() is/yields an lvalue, since T& && (via add_rvalue_reference) is collapsed to T&.
In the following code, the variadic constructor is called twice. How can I get the copy constructor to be called instead of the single argument version of the variadic constructor when appropriate?
#include <iostream>
struct Foo
{
Foo(const Foo &)
{
std::cout << "copy constructor\n";
}
template<typename... Args>
Foo(Args&&... args)
{
std::cout << "variadic constructor\n";
}
std::string message;
};
int main()
{
Foo f1;
Foo f2(f1); // this calls the variadic constructor, but I want the copy constructor.
}
This actually has nothing to do with the fact that the constructor is variadic. The following class with a non-variadic constructor template exhibits the same behavior:
struct Foo
{
Foo() { }
Foo(const Foo& x)
{
std::cout << "copy constructor\n";
}
template <typename T>
Foo(T&& x)
{
std::cout << "template constructor\n";
}
};
The problem is that the constructor template is a better match. To call the copy constructor, a qualification conversion is required to bind the non-const lvalue f1 to const Foo& (the const qualification must be added).
To call the constructor template, no conversions are required: T can be deduced to Foo&, which after reference collapsing (Foo& && -> Foo&), gives the parameter x type Foo&.
You can work around this by providing a second copy constructor that has a non-const lvalue reference parameter Foo&.
Just provide an exact-match overload, i.e. one with a non-const Foo&, in addition to the conventional copy constructor. Then you can delegate the call via an explicit cast:
Foo(Foo& other) : Foo{const_cast<Foo const&>(other)} { }