這取決於你如何演繹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
這裏沒有任務。 –