简体   繁体   中英

Error C2780 with variadic templates in forwarding to constructor C++

I tried to make emplace the same as it is done in std::vector, but I get errors that I can not understand. This code does NOT work, if I do not explicitly specify the type in the templates. I get the following error: error C2780: 'void AVLTree::emplace(Args &&...,bool)': expects 2 arguments - 3 provided In the process of trying to solve the problem I got the impression that the compiler takes the type of the first argument and substitutes it as the type of all arguments (except the last one). Also i tried to emplace_back with this arguments to std::vector and didn't get any errors. Compiler from last version MSVC 22.

main

AVLTree<Student> tree_balance, tree_non_balance;
tree_balance.emplace(QString("Anikeeva"),  QVector<int>{2, 4, 5, 2, 3}, true);

AVLTree.h

template <class... Args>
    void emplace(Args&& ...args, bool need_balance = true){
        INFO info(std::forward<Args>(args)...);
        add(std::move(info), need_balance);
    }
void add(INFO&& k, bool need_balance = true){
    root = insert(root, std::forward<INFO>(k), need_balance);
}
Node<INFO>* insert(Node<INFO>* p, INFO&& k, bool need_balance = true)
{
    if( !p ) {
        return new Node(std::forward<INFO>(k));
    }
    if( k<p->key )
        p->left = insert(p->left,std::forward<INFO>(k), need_balance);
    else
        p->right = insert(p->right,std::forward<INFO>(k), need_balance);
    if (need_balance) {
        return balance(p);
    }
    else {
        return p;
    }
}

Student.h

 Student(QString&& _FIO, std::vector<int>&& _arr) noexcept;
 Student(const QString& _FIO, const std::vector<int>& _arr) noexcept;

It's because the compiler cannot decide whether the boolean it sees in

tree_balance.emplace(QString("Anikeeva"),  QVector<int>{2, 4, 5, 2, 3}, true);

should be part of Args&&... . The error message you got is not very helpful, but the rule is that a parameter pack must be the last argument, so that the compiler can unambiguously take any remaining arguments to be part of the pack. So yes, putting the boolean first would be valid, but then it can't have a default value. Here is a strategy ("tag dispatch") you could use to get around this limitation:

struct unbalanced_t {};
inline constexpr unbalanced_t unbalanced {};

template <class... Args>
void emplace(Args&& ...args) {
    INFO info(std::forward<Args>(args)...);
    add(std::move(info), true);
}

// To be called as tree.emplace(unbalanced, ...);
template <class... Args>
void emplace(unbalanced_t, Args&& ...args) {
    INFO info(std::forward<Args>(args)...);
    add(std::move(info), false);
}

Variadic template is greedy, so default parameters afterward cannot be set explicitly.

If you can, use tuple instead (so more similar to std::pair 's constructor with std::piecewise_construct_t ):

template <class Tuple>
void emplace(Tuple&& args, bool need_balance = true)
{
    add(std::make_from_tuple<INFO>(forward<Tuple>(args)), need_balance);
}

With usage similar to:

tree_balance.emplace(std::tuple{QString("Anikeeva"), QVector<int>{2, 4, 5, 2, 3}} /*, true*/);

(And so, tuple can have bool as last argument)

Else, you can still drop bool need_balance parameter, and check last template argument or Args

template <class... Args>
void emplace(Args&& ...args)
{
    if constexpr (std::is_same_v<bool, decltype((args, ...))>){
        bool need_balance = (args, ...); // last value

        // Drop last parameter of pack with a custom create_tuple_without_last
        add(INFO(std::make_from_tuple<INFO>(create_tuple_without_last(std::forward<Args>(args)...)))), need_balance);
    } else {
        bool need_balance = false;
        add(INFO(std::forward<Args>(args)...), need_balance);
    }
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM