Ошибка компиляции при объявлении члена unique_ptr в классе

#c #g #c 17 #clang

Вопрос:

Это какая-то заводская лямбда. Класс, экземпляр которого должен быть создан лямбда, получает ссылку на другой класс в своем конструкторе. Если последний объявляет член unique_ptr, возникает действительно длинная ошибка:

 #include <memory>
#include <string>

struct A
{
    std::unique_ptr<int> foo;
};

struct B
{
    B(const std::string amp; str, Aamp;a) {}
};

template<typename T, typename ... Args>
auto registerType(const std::string amp; type, Argsamp;amp; ... args)
{

    return [args = std::make_tuple(std::forward<Args>(args) ...)]() mutable -> auto
    {
        return std::apply([](autoamp;amp; ... args){
            return std::make_unique<T>(args ...);
        }, std::move(args));
    };
}

int main()
{
    A a;
    registerType<B>("lala", "p1", a)();
}
 

Удаление элемента unique_ptr работает нормально.
Передача указателя на «a» вместо ссылки также работает нормально.

Вывод ошибок длинный, лучше увидеть его в действии:

https://godbolt.org/z/n896qMz59

Это происходит, по крайней мере, с:

  • g 9.2.0 и магистраль
  • лязг ствол

Есть какие-нибудь идеи?

Спасибо

Комментарии:

1. Ошибка в том, что: no matching function for call to 'std::tuple<const char*, A>::tuple(const char [3], Aamp;)

2. registerType<B>("lala", "p1", std::move(a))(); godbolt.org/z/W88hTrT48

Ответ №1:

Вы не можете скопировать unique_ptr А.

Но ты можешь передвинуть его:

 #include <utility>

// ...

registerType<B>("lala", "p1", std::move(a))();
 

или предотвратите std::decay , чтобы при использовании make_tuple не произошло:

 return [args = std::tuple<Args...>(std::forward<Args>(args) ...)]() mutable -> auto
 

Комментарии:

1. Однако вы все равно можете передать его в качестве ссылки, если сделаете std::ref(a) это, но это, несомненно, вызовет другие проблемы

2. Ах… так просто… Хорошо, я знаю, что не могу не скопировать unique_ptr, но не могли бы вы, пожалуйста, сказать мне, где в выводе ошибок, полном ошибок кортежа, есть хотя бы намек на это? И почему его копируют?

3. @osovan std::make_tuple уничтожает свои аргументы, удаляя ссылки и, следовательно, делая копию struct A того, что изначально было передано как a . видишь en.cppreference.com/w/cpp/utility/tuple/make_tuple

4. @osovan Вывод ошибок отличается для разных компиляторов — ни один из них не поддается легкому анализу. 🙂 Да, это распад внутри make_tuple . Я добавил опцию, чтобы предотвратить это.

5. @TedLyngmo Спасибо за добавление опции.