I have a variadic template with a variadic std::tuple as a member.
I declared the default assignment operator.
MSVC complains that the operator is deleted. I can still run the program.
It doesnt give the expected result.
So 3 Questions come to my mind:
Why is the default copy assignment operator not generated even if flagged as default?
Why can I run the programm still with the code posted?
How to declare a assignment operator for std::tuple<T...> template?
#include <string>
#include <iostream>
#include <tuple>
template<typename ...T>
class Variadic
{
public:
explicit Variadic(T... args)
:m_data{ std::move(args)... }
{
}
Variadic& operator=(const Variadic&) = default;
template<typename Type>
Type get_element() const
{
return std::get<Type>(m_data);
}
private:
std::tuple<T...> m_data{};
};
int main()
{
Variadic<int, std::string> tuple1{ 1, "a" };
Variadic<int, std::string> tuple2{ 2, "b" };
std::cout << tuple1.get_element<int>() << '\n'; // 1
std::cout << tuple1.get_element<std::string>() << '\n'; // a
std::cout << tuple2.get_element<int>() << '\n'; // 2
std::cout << tuple2.get_element<std::string>() << '\n'; // b
tuple2 = tuple1; // MSVC2017 gives warning here:
//function "Variadic<T...>::operator=(const Variadic<T...> &) [with T=<int, std::string>]"
//(declared at line 13) cannot be referenced -- it is a deleted function
// still it compiles ???
std::cin.get();
std::cout << tuple1.get_element<int>() << '\n'; // 1
std::cout << tuple1.get_element<std::string>() << '\n'; // a
std::cout << tuple2.get_element<int>() << '\n'; // expect 1 but 2 ?
std::cout << tuple2.get_element<std::string>() << '\n'; // expect b but a ?
}
Related
I'm working on my own smart pointer and I ran into some weird problems. The move assignment operator was not being called. So I wrote a test class and was able to reproduce the issue. The move assignment operator is not called but a copy assignment occurs (even when there is no copy assignment operator).
This is my test class
#include <utility>
#include <iostream>
struct tag_t {};
constexpr tag_t tag {};
template <typename T>
struct Foo {
Foo() noexcept
: val{} {
std::cout << "Default construct\n";
}
template <typename U>
Foo(tag_t, const U &val) noexcept
: val{val} {
std::cout << "Construct " << val << '\n';
}
~Foo() noexcept {
std::cout << "Destruct " << val << '\n';
}
template <typename U>
Foo(Foo<U> &&other) noexcept
: val{std::exchange(other.val, U{})} {
std::cout << "Move construct " << val << '\n';
}
template <typename U>
Foo &operator=(Foo<U> &&other) noexcept {
std::cout << "Move assign " << other.val << '\n';
val = std::exchange(other.val, U{});
return *this;
}
T val;
};
These are the tests
int main() {
{
Foo<int> num;
std::cout << "Value " << num.val << '\n';
num = {tag, 5};
std::cout << "Value " << num.val << '\n';
}
std::cout << '\n';
{
Foo<int> num;
std::cout << "Value " << num.val << '\n';
num = Foo<int>{tag, 5};
std::cout << "Value " << num.val << '\n';
}
return 0;
}
After running the tests, I get these results
Default construct
Value 0
Construct 5
Destruct 5
Value 5
Destruct 5
Default construct
Value 0
Construct 5
Move assign 5
Destruct 0
Value 5
Destruct 5
What baffles me is the output of the first test. The move assignment operator is not called but a copy assignment takes place. This results in 5 being destroyed twice. Not ideal when you're trying to make a smart pointer!
I'm compiling with Apple Clang with optimizations disabled. Can someone explain my observations? Also, how do I ensure that the move assignment operator is called in the first test?
template <typename U>
Foo &operator=(Foo<U> &&other) noexcept;
this cannot be called by ={ }.
Instead, Foo& operator=(Foo&&)noexcept is called.
Template methods are never special member functions. Explicitly default, delete or implement them.
I have a trivial allocator:
// alloc.h
#include <cstdlib>
#include <new>
#include <iostream>
template <class T>
struct Mallocator {
typedef T value_type;
Mallocator() {
std::cout << "default ctor is called" << std::endl;
}
template <class U> Mallocator(const Mallocator<U>&) {
std::cout << "copy ctor is called" << std::endl;
}
T* allocate(std::size_t n) {
std::cout << "Mallocator::allocate(size_t n) is called, n = " << n << " ";
if(n > std::size_t(-1) / sizeof(T)) throw std::bad_alloc();
if(T *p = static_cast<T*>(std::malloc(n*sizeof(T)))) {
std::cout << "return p = " << std::hex << (uintptr_t)p << std::dec << std::endl;
return p;
}
throw std::bad_alloc();
}
void deallocate(T* p, std::size_t n) {
std::cout << "Mallocator::deallocate(T *p, size_t n) is called, p = " << std::hex << (uintptr_t)p << std::dec << " n = " << n << std::endl;
std::free(p);
}
};
template <class T, class U>
bool operator==(const Mallocator<T>&, const Mallocator<U>&) { return true; }
template <class T, class U>
bool operator!=(const Mallocator<T>&, const Mallocator<U>&) { return false; }
And this is the client code (only one of A, B, C is used):
#include "alloc.h"
#include <vector>
#include <iostream>
using namespace std;
int main() {
Mallocator<int> a;
cout << "---instantiate---" << endl;
// vector<int, Mallocator<int>> v(a); // A
vector<int, Mallocator<int>> v{Mallocator<int>(a)}; // B
// vector<int, Mallocator<int>> v(Mallocator<int>(a)); // C
cout << "---push_back(1)---" << endl;
v.push_back(1);
cout << "---push_back(2)---" << endl;
v.push_back(2);
cout << "---push_back(3)---" << endl;
v.push_back(3);
cout << "---push_back(4)---" << endl;
v.push_back(4);
cout << "---push_back(5)---" << endl;
v.push_back(5);
cout << "---exiting---" << endl;
}
The output, no matter A or B is used, is always this:
default ctor is called
---instantiate---
---push_back(1)---
// omitted for brevity..
My question:
(1) if A is present, the allocator is just constructed once, that's understandable. But when B is present instead of A, apparently the copy constructor of Mallocator is called in B, but the output doesn't reflect this. Why?
(2) If B is present, which constructor of std::vector is called? In this reference, the only constructor that takes an initializer list doesn't look like this. And if I use C instead of B, it won't compile, and the error message of clang++ is not helping..
Eidt: I know this allocator is trivial but it is not the point of this question..
The code of "alloc.h" is adapted from here, at the end of the page.
1) Your "copy constructor" isn't one. A real copy constructor isn't a template. Every class gets a copy constructor implicitly declared if it doesn't declare one itself. Mallocator<int> doesn't declare a real copy constructor, so one gets implicitly declared and defined for you. Since your class is empty, that copy constructor does nothing and prints nothing (and, thanks to the overload resolution rules, is selected to copy the allocator over your constructor template).
2) List-initialization can call non-initializer-list constructors if no initializer-list constructor is viable. B ends up calling the same constructor as A. Your C is a case of the most-vexing-parse.
I want to create a temporary copy of a const object and use it in a non-const way:
struct S {
S& f() { return *this; }
};
int main() {
const S a{};
S{a}.f(); // Error on this line
return 0;
}
Using msvc (Visual Studio 2017, C++14), I get this error:
Error C2662 'S &S::f(void)': cannot convert 'this' pointer from 'const S' to 'S &'
If I change the brace initialization to classic initialization, it works:
S{a}.f(); // Does not work
S(a).f(); // Works
Both variants compile fine in gcc. Am I missing something or is this a compiler bug?
Seems like another MSVC bug.
S{a} is deduced as const struct S, and that alone is bug.
#include <string>
#include <iostream>
template < class T >
std::string type_name()
{
std::string p = __FUNCSIG__;
return p.substr( 106, p.length() - 106 - 7 );
}
struct S {
S& f() { return *this; }
};
int main() {
const S a{};
//S{a}.f(); // Error on this line
std::cout << type_name<decltype(S{a})>() << std::endl;
return 0;
}
Output:
const struct S
It looks like a compiler bug, or the result of a weird optimization, because this variation of original code that only make ctors and dtor with side effects compiles fine using MSVC:
#include <iostream>
struct S {
S(const S& other) {
std::cout << "Copy ctor " << &other << " -> " << this << std::endl;
}
S() {
std::cout << "Default ctor " << this << std::endl;
}
~S() {
std::cout << "Dtor " << this << std::endl;
}
S& f() { return *this; }
};
int main() {
const S a{};
std::cout << &a << std::endl;
S{a}.f();
return 0;
}
Compilation is successful and output is:
Default ctor 0306FF07
Copy ctor 0306FF07 -> 0306FF06
Dtor 0306FF06
Dtor 0306FF07
I just made a wrapper for move and copy operations to inject into code to see which is called in case of default implementations. I'm getting close to understanding when what is called but would like to double check at times.
I'm not sure if method 1 of using T::T; is better for the constructors than method 2 of forwarding the arguments like unique_ptr? I found it in this thread Forwarding all constructors in C++0x
In move constructor and assignment I use std::move to pass onto the super class. Should this be std::forward and if so, how? I get errors trying to use it.
#ifndef MOVECOPY_OBSERVER_H
#define MOVECOPY_OBSERVER_H
#include <iostream>
template<class T>
class MoveCopyObserver : public T {
public:
//1: Use "using" for constructors
//From https://stackoverflow.com/questions/3119929/forwarding-all-constructors-in-c0x
using T::T;
//2: Forward all args, unique_ptr style.
/*
template<typename... Args>
MoveCopyObserver(Args&&... args)
: T(std::forward<Args>(args)...)
{
};*/
// *************************************************************************
virtual ~MoveCopyObserver() = default;
// *************************************************************************
MoveCopyObserver(const MoveCopyObserver& other)
: T(other)
{
std::cout << "Copy constructor " << typeid(T).name() << std::endl;
}
// *************************************************************************
MoveCopyObserver(MoveCopyObserver && other)
: T(std::move(other)) //3: std::forward instead?
{
std::cout << "Move constructor " << typeid(T).name() << std::endl;
}
// *************************************************************************
MoveCopyObserver& operator=(const MoveCopyObserver& other)
{
T::operator=(other);
std::cout << "Copy assignment " << typeid(T).name() << std::endl;
return *this;
}
// *************************************************************************
MoveCopyObserver& operator=(MoveCopyObserver&& other)
{
T::operator=(std::move(other)); //3: std::forward instead?
std::cout << "Move assignment " << typeid(T).name() << std::endl;
return *this;
}
};
#endif //MOVECOPY_OBSERVER_H
The usage would be on the stack or through smart pointers, like so:
class A {
public:
A(std::string ss)
{
s = ss;
}
void f()
{
std::cout << "\"" << s << "\"" << std::endl;
}
private:
std::string s;
};
A a("Test instance");
a.foo();
MoveCopyObserver<A> b("Another instance");
b.foo();
I'm trying to make a function that mimics Python's with statement but I've run into some interesting behavior that I don't quite understand.
With the following program:
#include <iostream>
struct foo {
foo() { std::cout << "foo()" << std::endl; }
~foo() { std::cout << "~foo()" << std::endl; }
};
auto make_foo() -> foo {
return {};
}
template <typename T, typename F>
auto with(T&& t, F&& fn) -> void {
fn(std::forward<T>(t));
}
auto main() -> int {
std::cout << "before" << std::endl;
with(make_foo(), [](auto f) {
std::cout << "during" << std::endl;
});
std::cout << "after" << std::endl;
}
When compiled under with the clang provided by Xcode 6.3 and -std=c++14 and run I get the following output:
before
foo()
during
~foo()
~foo()
after
Does anybody know why I am getting two ~foo()'s in my output?
Here are the two objects:
with(make_foo(), [](auto f) {
1^^^^^^^^^ 2^^^^^^
There is the object returned by make_foo(), and the function argument f.
If you pass by reference (change to auto&& f) then you will only see evidence of one object.
There's no creation message because this is created by copy/move construction and you do not have any output in those constructors.
Note that there may be more objects inside make_foo() but your compiler is doing copy elision.
Your destructor calls don't appear to be matched with constructor calls simply because you aren't tracing copy/move constructors. If we add the tracing like so:
struct foo {
foo() { std::cout << "foo()" << std::endl; }
~foo() { std::cout << "~foo()" << std::endl; }
foo(const foo&) { std::cout << "foo(const foo&)" << std::endl; }
foo(foo&&) { std::cout << "foo(foo&&)" << std::endl; }
};
our output is now:
before
foo()
foo(foo&&)
during
~foo()
~foo()
after
The reason for the move-construction is that your lambda takes its parameter by value:
[](auto f) {
// ^^^^^^
std::cout << "during" << std::endl;
}
If you don't want the copy, take by reference-to-const, or maybe even forwarding reference.
This works for me by accepting an r-reference in the lambda function parameter to prevent a copy being made:
#include <iostream>
struct foo {
foo() { std::cout << "foo()" << std::endl; }
~foo() { std::cout << "~foo()" << std::endl; }
};
auto make_foo() -> foo {
return {};
}
template <typename T, typename F>
auto with(T&& t, F&& fn) -> void {
fn(std::forward<T>(t));
}
auto main() -> int {
std::cout << "before" << std::endl;
with(make_foo(), [](auto&&) { // r-reference!
std::cout << "during" << std::endl;
});
std::cout << "after" << std::endl;
}
New Improved Output:
before
foo()
during
~foo()
after