名为 Queue 的类的实例不能以某些方式初始化,但可以以其他方式初始化,这两种方式对我来说似乎几乎相同

An instance of a class named Queue can't be initialized in some ways but can in others and both ways seem almost identical to me

提问人:Chris 提问时间:6/23/2023 最后编辑:Nicol BolasChris 更新时间:6/23/2023 访问量:64

问:

我一直在努力寻找一个更好的标题。 错误如下:错误 C2440:“initializing”:无法从“initializer list”转换为“Queue” 这似乎是我遇到的一个非常常见的错误,我希望这是一个常见的错误。 问题发生在运算符中<< :

template <typename T>
std::ostream& operator<<(std::ostream& out, bsTree<T>& tree) {
    if (!tree.m_root)
        return out;
    using Nodeptr = typename bsTree<T>::Node*;
    
    Queue<Nodeptr> q{ tree.m_root }; //this line right here
    Nodeptr current{tree.m_root};
    while (!q.empty()) {
        current = q.front();
        std::cout << current->data << '\n';
        if (current->left)
            q.push(current->left);
        if (current->right)
            q.push(current->right);
        q.pop();
    }
    return out;
}

在队列中:

template <std::same_as<T>... Args>
    Queue(Args&&... args) {
        push(std::forward<T>(args)...);
    }

似乎问题在于编译器推断为 T 的内容与实际的参数类型之间存在一些差异。 这是 Queue 中唯一的构造函数,另一个不带任何参数。 如果 std::same_as 被替换为 typaname,问题就会消失,所以这有一些东西,我想知道它是什么。与 std::same_as 一样,如果我使用函数返回树根节点的地址,那么它就可以工作,但是如果它返回对保存根地址的私有成员变量的引用,那么它又不起作用。 奇怪的是,当我在运算符中定义一个 Nodeptr 变量时<<并使用 tree.m_tree 初始化它,然后使用它来初始化不起作用的队列。这似乎与使用函数获取树的根地址相同(例如,如果我没有将运算符<< bsTree 的朋友,那么我将不得不使用一个函数),但它不起作用,我很好奇为什么它不会。 使用简单的 typename...似乎解决了所有这些情况下的所有问题。 我想我现在会使用它,我会回来看看发生了什么:)

我试过打字名...而不是 std::same_as...我本以为问题会消失...... 我也只是有一个想法,即编译器可能会创建引用。为了测试这一点,我在运算符中尝试了这个<<

using Nodeptr = typename bsTree<T>::Node*;
    Nodeptr current{ tree.m_root };
    Nodeptr temp{ current };
    Queue<Nodeptr> q{ std::move(temp)};

所以这奏效了,所以我想我应该使用一些 std 概念来删除引用等来获得裸露的类型。 我试过这个:

template <std::same_as<std::remove_cvref_t<T>>... Args>
    Queue(Args&&... args) {
        push(std::forward<T>(args)...);
    }

我想由于某种原因仍然不正确,这给出了相同的错误(删除 std::move 后) 欢迎任何建议,提前谢谢大家。

C++ C++20 模板参数推导

评论

1赞 dalfaB 6/24/2023
最后一个似乎是要采取的想法,但我认为概念符号在这里不适用。尝试。template<class...Ts>Queue(Ts&&... args) requires (std::conjunction_v<std::is_same<T,std::remove_reference_t<Ts>>...>) {push( std::forward<Ts>(args)... );}
0赞 Chris 6/24/2023
这个我没有尝试,而是因为我尝试了另一个答案:模板<typename...Args> 需要 (std::same_as<T, std::remove_cvref_t<Args>> && ...)队列(Args&&...args) { push(std::forward<T>(args)...);如果我理解正确的话,这就是 std::conjunction_v 的用途,所以它可能是一样的。我只是尝试了一下,从字面上复制粘贴了这个,然后注释掉了前一个以使用这个,但我得到了同样的错误,我想知道为什么......我将is_same更改为 same_as,remove_reference_t更改为 std::remove_cvref_t 试图使其与其他解决方案相同,并且是错误的
0赞 Chris 6/24/2023
但是这有效:std::conjunction_v<std::is_same<T,std::remove_cvref_t<Ts>>...>我应该找一些东西来阅读以更好地理解概念,但在这里我不明白为什么这有效而不是另一个。

答:

0赞 康桓瑋 6/23/2023 #1

所以这有效,所以我想我应该使用一些标准概念来删除 引用等来获取裸类型。

您可以使用 -子句进行检查requires

template <typename... Args>
  requires (std::same_as<T, std::remove_cvref_t<Args>> && ...) 
Queue(Args&&... args) {
  push(std::forward<T>(args)...);
}

评论

0赞 Chris 6/24/2023
这行得通,非常感谢......我试过这个: requires (std::same_as<T, std::remove_cvref_t<std::remove_pointer_t<Args>>> && ...) 也删除指针,我不知道是否需要,在这种情况下可能不是,因为两者都有效。我想概念在这里并不直接适用,而是在 requires-clause 中......我真的不知道有什么区别