这取决于你如何演绎T
。例如:
template<class T>
foo1<T> make_foo1(T&& t) {
return std::forward<T>(t);
}
在这种情况下,在foo1<T>
的T
是转发引用,您的代码将无法编译。
std::vector<int> bob{1,2,3};
auto foo = make_foo1(bob);
上面的代码从bob
默默地移入std::vector<int>&
构造内foo1<std::vector<int>&>
。
这样做与foo2
一样可行。你会得到一个foo2<std::vector<int>&>
,它将持有对bob
的参考。
当你写一个模板时,你必须考虑T
这个类型是什么意思。如果您的代码不支持将其作为参考,请考虑使用static_assert
或SFINAE来阻止该情况。
template <typename T>
struct foo1 {
static_assert(!std::is_reference<T>{});
T t_;
foo1(T&& t) :
t_{ std::move(t) }
{
}
};
此代码生成一个合理的错误消息。
你可能会认为现有的错误信息是好的,但它只是好的,因为我们搬进了T
。
template <typename T>
struct foo1 {
static_assert(!std::is_reference<T>{});
foo1(T&& t)
{
auto internal_t = std::move(t);
}
};
这里只static_assert
确保我们T&&
是实际右值。
但足够与这个理论列表的问题。你有一个具体的。
到底这可能是想你想:
template <class T> // typename is too many letters
struct foo1 {
static_assert(!std::is_reference<T>{});
T t_;
template<class U,
class dU=std::decay_t<U>, // or remove ref and cv
// SFINAE guard required for all reasonable 1-argument forwarding
// reference constructors:
std::enable_if_t<
!std::is_same<dU, foo1>{} && // does not apply to `foo1` itself
std::is_convertible<U, T> // fail early, instead of in body
,int> = 0
>
foo1(U&& u):
t_(std::forward<U>(u))
{}
// explicitly default special member functions:
foo1()=default;
foo1(foo1 const&)=default;
foo1(foo1 &&)=default;
foo1& operator=(foo1 const&)=default;
foo1& operator=(foo1 &&)=default;
};
或者,这只是在99/100情况一样好简单的情况:
template <class T>
struct foo1 {
static_assert(!std::is_reference<T>{});
T t_;
foo1(T t) :
t_{ std::move(t) }
{}
// default special member functions, just because I never ever
// want to have to memorize the rules that makes them not exist
// or exist based on what other code I have written:
foo1()=default;
foo1(foo1 const&)=default;
foo1(foo1 &&)=default;
foo1& operator=(foo1 const&)=default;
foo1& operator=(foo1 &&)=default;
};
作为一般规则,这更简单的技术比完美的转发技术的结果只有更多的代价和复杂性。它允许{}
初始化T t
参数给你的构造函数,这很好。
我会让知道背后原因的人发布一个完整的答案,但如果你想快速的是/否,我总是被告知你的'foo2'例子是正确的。 –
这些不是“通用”(转发)引用 - 如果您需要转发引用,则需要模板化构造函数。 – Holt
这里没有任务。 –