重载,使用r值refs,类模板的构造函数
Overloading, with r-value refs, constructor of class template
虽然最初它似乎在工作,但当通过maker函数调用(以进行模板参数推导(时,两个重载的存在,一个带有T const&
,一个带T&&
,中断了编译:
#include <iostream>
#include <utility>
#include <functional>
using namespace std;
// -----------------------------------------------
template<typename T, typename F>
struct Test
{
T m_resource;
F m_deleter;
Test(T&& resource, F&& deleter)
: m_resource(move(resource)), m_deleter(move(deleter))
{
}
Test(T const& resource, F const& deleter)
: m_resource(resource), m_deleter(deleter)
{
}
};
// -----------------------------------------------
// -----------------------------------------------
template<typename T, typename F>
Test<T, F> test(T&& t, F&& f)
{
return Test<T, F>(move(t), move(f));
}
template<typename T, typename F>
Test<T, F> test(T const& t, F const& f)
{
return Test<T, F>(t, f);
}
// -----------------------------------------------
int main()
{
// construct from temporaries --------------------
Test<int*, function<void(int*)>> t(new int, [](int *k) {}); // OK - constructor
auto tm = test(new int, [](int *k){}); // OK - maker function
// -----------------------------------------------
// construct from l-values -----------------------
int *N = new int(24);
auto F = function<void(int*)>([](int *k){});
Test<int*, function<void(int*)>> tt(N, F); // OK - construction
auto m = test(N, F); // Error - maker function
// -----------------------------------------------
return 0;
}
有什么想法吗?
template<typename T, typename F>
Test<T, F> test(T&& t, F&& f)
{
return Test<T, F>(move(t), move(f));
}
此函数模板有两个参数,它们是通用引用。模板类型参数&&
形式的函数模板的参数遵循特殊的推导规则:如果参数是左值,则模板类型参数被推导为左值引用类型。如果参数是右值,则推导的类型不是引用。
auto m = test(N, F);
int* N
和function<void(int*)> F
是左值,因此对于上面的函数模板,T
被推导为int*&
,F
被推导为function<void(int*)>&
。应用参考折叠,参数T&&
变为int*& &&
并折叠为int*&
(类似于F&&
(。
因此,类模板是用引用类型(T == int*&
、F == function<void(int*)>&
(实例化的。在类模板内部,
Test(T&& resource, F&& deleter)
Test(T const& resource, F const& deleter)
将产生相同的签名,因为int*& &&
和int*& const&
都被折叠为int*&
,类似于F
。
注意,当参数不是常量时,具有参数int*&
的函数比具有参数int* const&
的函数更可取。因此,功能模板
template<typename T, typename F>
Test<T, F> test(T const& t, F const& f)
将不会在出现错误的行中使用。一般来说,通用参考参数是非常贪婪的。
典型的解决方案是使用完美转发,如Jonathan Wakely的回答所述。
template<typename T, typename F>
Test<T, F> test(T&& t, F&& f)
{
return Test<T, F>(move(t), move(f));
}
这个函数处理左值和右值。当您调用test(N, F)
时,参数是非常值,因此使用上面的重载,而不是test(const T&, const F&)
。这会将您的类模板实例化为Test<int*&, std::function<void(int*)>&>
,这不是您想要的。
你只需要一个制造商功能:
template<typename T, typename F>
auto test(T&& t, F&& f)
-> Test<typename std::decay<T>::type, typename std::decay<F>::type>
{
return { std::forward<T>(t), std::forward<F>(f) };
}
这将接受左值和右值的任何组合,它将确保您返回所需的类型,并且它将使用它们的原始值类别将参数完美地转发给Test
构造函数。
您应该阅读/观看C++11 中的通用参考
我认为这是因为自动类型猜测失败,因为这行代码显示prog.cpp:18:2: error: ‘Test<T, F>::Test(const T&, const F&) [with T = int*&; F = std::function<void(int*)>&]’ cannot be overloaded
。您的编译器试图用int*&而不是int*。我敢肯定,如果你明确指定类型(我理解你想要避免的(,它会很好地工作。
我真的不明白你为什么把这个放在你的制造商功能中
Test<T, F> test(T&& t, F&& f)
{
return Test<T, F>(move(t), move(f));
}
t
和f
已经是右值引用,所以您不需要对它们调用move
- "error: no matching function for call to"构造函数错误
- C++17复制构造函数,在std::unordereded_map上进行深度复制
- 如果C++类在类方法中具有动态分配,但没有构造函数/析构函数或任何非静态成员,那么它仍然是POD类型吗
- 为什么在没有显式默认构造函数的情况下,将另一个结构封装在联合中作为成员的结构不能编译
- 为什么在C++中使用私有复制构造函数与删除复制构造函数
- 选择要调用的构造函数
- 如何委托派生类使用其父构造函数?
- 构造函数正在调用一个使用当前类类型的函数
- 没有用于初始化C++中的变量模板的匹配构造函数
- 初始化具有非默认构造函数的std::数组项的更好方法
- 当从函数参数中的临时值调用复制构造函数时
- 在c++构造函数中使用随机字符串生成器
- 一对向量构造函数:初始值设定项列表与显式构造
- 从构造函数抛出异常时如何克服内存泄漏
- 我不明白为什么我声明一个空的内部结构并将其传递给构造函数
- 继承:构造函数,初始化C++11中基类的类C数组成员
- 具有默认模板类型的默认构造函数的类型推导
- 使用dynamic_cast和构造函数时出错
- 在c++中使用向量时,如何调用构造函数和析构函数
- 重载,使用r值refs,类模板的构造函数