gpt4 book ai didi

c++ - 模板模板推导给我一个通用引用的错误

转载 作者:行者123 更新时间:2023-12-02 10:22:38 25 4
gpt4 key购买 nike

为了真正理解 C++17 折叠表达式,我为容器编写了一个函数 append :

#include <iostream>
#include <vector>
#include <list>

// fold expression
template<typename T, typename U, template<typename> typename... Args>
inline void append_impl(std::vector<T>& v, Args<U>... args) noexcept
{
static_assert((std::is_constructible_v<T,U>));
std::cout << "append_impl version one " << std::endl;
(v.insert(std::end(v),
std::begin(args),
std::end (args)), ...);
}

//fold expression
template<typename T, typename... Args>
inline void append_impl(std::vector<T>& v, Args&&... args) noexcept
{
static_assert((std::is_constructible_v<T, Args&&> && ...));
std::cout << "append_impl version two " << std::endl;
(v.push_back(std::forward<Args>(args)), ...);
}
// fold expression
template<typename T, typename... Args>
inline void append(std::vector<T>& v, Args&&... args) noexcept
{
(append_impl(v, args), ...);
}


int main()
{
std::vector<int> a = {1,2};
std::vector<int> b = {3,4};
std::vector<int> c = {5,6};
std::list<int> d = {15,16};

append(a,b,c, std::vector<int>{8,9}, 10, 11, 12, 13, 14, d);


for(const auto& e : a)
{
std::cout << e << " ";
}
std::cout << std::endl;

return 0;
}
这工作正常并给我结果:

append_impl version one

append_impl version one

append_impl version one

append_impl version two

append_impl version two

append_impl version two

append_impl version two

append_impl version two

append_impl version one

1 2 3 4 5 6 8 9 10 11 12 13 14 15 16


但我对这段代码有一个问题:
  • 在 append_impl 的第一个版本中,args 是按拷贝传递的。我想使用通用引用(在 Scott Meyers sens 中)来避免这种情况,但是 append_impl(std::vector<T>& v, Args<U>&&... args)给我一个编译错误。
  • rog.cc: In instantiation of 'void append_impl(std::vector<T>&, Args&& ...) [with T = int; Args = {std::vector<int, std::allocator<int> >&}]':
    prog.cc:18:15: required from 'void append(std::vector<T>&, Args&& ...) [with T = int; Args = {std::vector<int, std::allocator<int> >&, std::vector<int, std::allocator<int> >&, std::vector<int, std::allocator<int> >, int, int, int, int, int, std::__cxx11::list<int, std::allocator<int> >&}]'
    prog.cc:29:59: required from here
    prog.cc:10:3: error: static assertion failed
    10 | static_assert((std::is_constructible_v<T, Args&&> && ...));
    | ^~~~~~~~~~~~~
    prog.cc:12:15: error: no matching function for call to 'std::vector<int>::push_back(std::vector<int>&)'
    12 | (v.push_back(std::forward<Args>(args)), ...);
    为什么以及我可以做些什么来避免复制?

    最佳答案

    您的代码存在以下问题:

    你忘了#include<type_traits> .

    您忘记转发 append :

    (append_impl(v, std::forward<Args>(args)), ...);
    append_impl 中的模板模板参数有点问题。因为写成 template<typename> typename... Args它假定传递给它的模板采用一个模板参数。 std::list (和其他容器)采用多个模板参数(尽管其他是默认的)。尚不完全清楚这样的模板是否应该对您的模板模板参数有效。

    GCC 接受它,而 Clang 不接受(参见 https://godbolt.org/z/LY9r-k )。我认为在 resolution of CWG issue 150 之后GCC 接受代码是正确的,但我没有仔细检查。

    无论如何,通过将参数更改为 template<typename...> typename... Args 可以轻松避免此问题。 ,它接受具有任意多个参数的模板。

    您的实际问题:通用引用(或转发引用)仅在模板参数 T 时才有效直接用作 T&&在函数参数中。如果在函数参数的模板参数中使用它,则引用折叠规则导致通用引用的转发行为不适用。
    Args<U>&&... args始终是右值引用,无论是什么引用限定 U拥有。

    因此,如果您尝试将左值引用转发到始终需要右值引用的此函数模板,则您的代码不会编译。

    您可能应该为右值引用编写一个模板重载,为 const 编写一个模板重载。左值引用:
    template<typename T, typename U, template<typename...> typename... Args>
    inline void append_impl(std::vector<T>& v, const Args<U>&... args) noexcept

    template<typename T, typename U, template<typename...> typename... Args>
    inline void append_impl(std::vector<T>& v, Args<U>&&... args) noexcept

    然后你有一个问题,一个非 const左值引用将更适合采用 Args&& 的第二个模板重载直接,所以你需要添加另一个重载:
    template<typename T, typename U, template<typename...> typename... Args>
    inline void append_impl(std::vector<T>& v, Args<U>&... args) noexcept
    {
    append_impl(v, std::as_const(args)...);
    }

    (需要 #include<utility> )

    通常,您尝试识别容器的方式不是保存。对于由模板类形成且不是容器的所有类型,它将失败。

    相反,您可以通过 SFINAE 在您要使用的容器接口(interface)上选择正确的函数重载,即 std::begin(arg) , std::end(arg)insert称呼。您可以使用 expression SFINAE这样做。

    或者,您可以为这些表达式的格式良好编写类型特征,并为 append_impl 使用单个重载。根据 if constexpr 选择实现检查类型特征。

    在 C++20 中,这将以更简单的方式使用概念来实现。

    似乎也没有任何理由 append_impl需要 Args作为参数包。它只用一个参数调用 Args .

    关于c++ - 模板模板推导给我一个通用引用的错误,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/59456970/

    25 4 0
    Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
    广告合作:1813099741@qq.com 6ren.com