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;
CodePudding user response:
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);
}
CodePudding user response:
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);
}
}