模板模板推导给了我一个通用引用的错误
template template deduction give me an error with universal references
为了真正理解C++17折表达式,我为容器编写了一个函数附加:
#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版本一
append_impl版本一
append_impl版本一
append_impl版本二
append_impl版本二
append_impl版本二
append_impl版本二
append_impl版本二
append_impl版本一
1 2 3 4 5 6 8 9 10 11 12 13 14 15 16
但是我从这段代码中有一个问题:
- 在 append_impl 的第一个版本中,args 是按副本传递的。我想使用通用引用(在斯科特·迈耶斯的意义上(来避免这种情况,但
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(。我认为在解决 CWG 问题 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 选择正确的函数重载,即std::begin(arg)
,std::end(arg)
和insert
电话。您可以使用表达式 SFINAE 来执行此操作。
或者,您可以为这些表达式的格式良好编写类型特征,并对根据检查类型特征if constexpr
选择实现的append_impl
使用单个重载。
在C++20中,这将以更简单的方式使用概念来实现。
似乎也没有任何理由append_impl
Args
作为参数包。它只用一个参数来调用Args
。
- 返回一个引用C++中另一个类对象的对象的有效方法
- 返回一个C++引用变量 VS 返回一个变量
- 有没有办法重写一个返回指向 istringstream 的指针的函数,而不是返回一个引用?
- 如何使用只有一个引用参数的可变长度参数列表
- 为什么重载 ostream 的运算符<<需要一个引用"&"?
- C++ const-cast 一个引用
- 嵌套绑定到成员,需要一个指针,得到了一个引用.做什么
- r-Rcpp可以公开一个引用同一类的C++类方法
- 有没有办法获取对 MFC 对话框中所有子窗口或控件的引用,因为我有一个引用该控件的 CWnd 对象
- C++一个引用的多个转发:首先复制,然后移动
- 在C++中,我是否可以声明一个引用,以表明没有任何内容会修改它
- 将一个对象传递给一个引用函数,即面试测试
- 需要解释才能理解一个引用的例子
- 一个好的C++编译器会优化一个引用吗
- 我有一个引用,想调用一个需要 boost::shared_ptr 的函数
- 在C++中有一个引用成员有什么问题?
- *return如何隐式返回一个引用
- 为什么我得到一个未对齐指针的警告,而不是一个引用
- 是否可以在不同位置为一个引用分配不同的值
- 编写一个引用类型的函数,用作l值来处理它的r值