虽然最初似乎是working,但是当通过制造商函数调用(具有模板参数推导)时,存在两个重载,一个带有T const&
,一个带有T&&
breaks compilation:
#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;
}
有什么想法吗?
答案 0 :(得分:6)
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
构造函数。
您应该阅读/观看Universal References in C++11
答案 1 :(得分:6)
template<typename T, typename F>
Test<T, F> test(T&& t, F&& f)
{
return Test<T, F>(move(t), move(f));
}
此功能模板有两个通用引用参数。 template-type-parameter &&
形式的函数模板的参数遵循特殊推导规则:如果参数是左值,则模板类型参数推导为左值引用类型。如果参数是rvalue,则推导出的类型不是引用。
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
。
请注意,当参数不是const时,具有参数int*&
的函数优先于具有参数int* const&
的函数。因此,功能模板
template<typename T, typename F>
Test<T, F> test(T const& t, F const& f)
不会在出现错误的行中使用。通常,通用引用参数非常贪婪。
典型的解决方案是使用<{3}} Jonathan Wakely中所述的完美转发。
答案 2 :(得分:0)
我认为这是因为自动类型猜测失败了,因为这句话说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 *&amp;来实例化它。而不是int *。我非常确定你是否明确指定了类型(我明白你想要避免什么)它会正常工作。
而且我真的不明白为什么你把它放在你的制造商功能中
Test<T, F> test(T&& t, F&& f)
{
return Test<T, F>(move(t), move(f));
}
t
和f
已经是左值参考,因此您不需要调用它们移动