15

I have an object which holds a unique_ptr and as such can't be copy constructed without doing a deep copy (which I don't want).

I'd like to have std::any hold that object, but the only alternatives I've found is to either make std::any hold a pointer, which adds a useless indirection, or make my object have a unique ptr. The code bellow will hopefully illustrate my point:

//Compiled with clang++ -std=c++2a; clang version 5.0.0
#include <iostream>
#include <any>
#include <memory>

struct A {
        std::unique_ptr<int> m = std::make_unique<int>(11);
        A(A&& a): m(std::move(a.m)) {}
        A() {}
};

struct B {
        std::shared_ptr<int> m = std::make_shared<int>(11);
};


template<class T>
void use_any_ptr() {
        std::any a{new T{}};
        std::cout << *(std::any_cast<T*>(a)->m) << std::endl;
}

template<class T>
void use_any() {
        std::any a{T{}};
        std::cout << *(std::any_cast<T>(a).m) << std::endl;
}


int main() {
        use_any_ptr<A>(); // Workaround using a pointer
        use_any<B>(); // Workaround using shared pointer
        use_any<A>(); // Breaks because A has no cc no matching constructor for initialization of 'std::any'
}

As far as I understand the construction of std::any seems to require copying of the object, however I'm unsure why the object couldn't simply be moved.

Is there a way to work around this ? That is, other than using a shared_ptr, which means I'm basically expressing the "wrong thing" for the sake of creating the any object or passing std::any a pointer (which is an unneeded level of indirection, since std::any holds a void pointer to the type to being with as far as I can tell).

Is there a different implementation of any that could use the move ctr at creation instead of the copy ctr ?

Or am I being silly and not understanding the "real" problem here ?

Enlico
  • 23,259
  • 6
  • 48
  • 102
George
  • 3,521
  • 4
  • 30
  • 75

1 Answers1

4

Or am I being silly and not understanding the "real" problem here ?

The problem is simple here: std::any has a copy constructor and thus it requires the object contained is copy constructible.

The way you can work around it is by defining a custom object that contains the pointer, that is copyable and that implements a proper logic for it (whatever proper means in your case, I can't see how you could copy an std::any and thus an object contained there that owns an std::unique_ptr, but maybe you can get away with it somehow in your case).

Otherwise rethink your design and try to get rid of the need of an std::any. It hasn't much uses indeed and probably you can get the same with a bit of template machinery or an std::variant or whatever

skypjack
  • 49,335
  • 19
  • 95
  • 187
  • Ok, well, in that case I misunderstood the issue. I thought std::any's construction required a copy. I assume std::any would work without copy construction though, right ? So should I just re-write or use an impl without the copy ctr ? Or is there a way to remove the copy_ctr off a class ? – George Oct 20 '17 at 16:34
  • 1
    @George It requires the type you use to construct it is copy constructible. It checks it using traits (namely `std::is_copy_constructible`). – skypjack Oct 20 '17 at 16:36
  • @George No way to remove the requirement from `std::any`. You can either implement _your_ `any` or rethink your design. I would go for the second actually. – skypjack Oct 20 '17 at 16:37
  • 1
    Yes, I get that. But if I never use the copy ctr of std::any I could in theory use it without needing the copy ctr (and implicitly not need the cctr of the object held inside). – George Oct 20 '17 at 16:38
  • @George C++ doesn't work this way. The compiler cannot know you are not going to use the copy constructor in another compilation unit. – skypjack Oct 20 '17 at 16:39
  • I'd rather not implement std::any myself, however I need a vector of objects that can hold "any" type and I can't find a work around. One variant would be to use std::tuple but since the ordering of the types is not always the same and the size can vary using std::tuple becomes a pain because it's not made to be accessed with a runtime index – George Oct 20 '17 at 16:39
  • 7
    @George: `any` is not a template on the type it takes. So `any` *statically* has a copy constructor. It doesn't matter if you call it or not, it *must exist*. And therefore it must have an implementation. And that implementation must be able to work with any type that can be stored within it. Therefore, `any` must *statically* require its contents to have a copy constructor. – Nicol Bolas Oct 20 '17 at 16:39
  • @George look for type erasure to accomplish that. – skypjack Oct 20 '17 at 16:40
  • @NicolBolas Indeed clang uses `std::is_copy_constructible` on construction to check the type involved (well, I'm testing it on mobile, I could be wrong, but it seems it's doing that). – skypjack Oct 20 '17 at 16:42
  • It may be worth elaborating NicolBolas' comment in the answer, since it is quite important. This sentence alone: _"The problem is simple here: `std::any` has a copy constructor and thus it requires the object contained is copy-constructible."_ is not much useful by itself. `std::vector` also has a copy constructor and the contained objects do not need to be copy-constructible. – Daniel Langr Jun 11 '21 at 07:55
  • @DanielLangr well, technically speaking, you can implement [something similar to `std::any`](https://github.com/skypjack/entt/blob/master/src/entt/core/any.hpp) that also works for non-copyable types. As long as you can correctly define a policy for this case, you can use `std::is_copy_constructible` and the like. The actual problem is: what's a proper policy for this case? A general purpose one doesn't really exist, so dropping the non-copyable types support seems reasonable to me for an implementation that belongs to the standard library. – skypjack Jun 11 '21 at 11:15