香草薩特的回到基礎! CppCon的現代C++演示文稿討論了傳遞參數的不同選項,並比較了它們的性能與易於編寫/教學。 「高級」選項(在所有情況下提供最佳的性能測試,但太難對於大多數開發者編寫)是完美轉發,給定(PDF, pg. 28)的例子:完美轉發設置器上的'enable_if`約束是什麼?
class employee {
std::string name_;
public:
template <class String,
class = std::enable_if_t<!std::is_same<std::decay_t<String>,
std::string>::value>>
void set_name(String &&name) noexcept(
std::is_nothrow_assignable<std::string &, String>::value) {
name_ = std::forward<String>(name);
}
};
的示例使用轉發的模板函數參考,其中使用enable_if
約束了模板參數String
。但是這個約束似乎是不正確的:似乎是說這種方法只有在String
類型不是std::string
時纔可以使用,這是沒有意義的。這意味着std::string
成員可以使用設置,除和std::string
之外的任何值。
using namespace std::string_literals;
employee e;
e.set_name("Bob"s); // error
一種解釋我考慮的是,有一個簡單的錯字和約束的用意是std::is_same<std::decay_t<String>, std::string>::value
而不是!std::is_same<std::decay_t<String>, std::string>::value
。然而,這意味着制定者不適用於(例如)const char *
,並且顯然它打算使用這種類型,因爲這是在演示文稿中測試的案例之一。
在我看來,正確的約束更像是:
允許任何可以被分配給成員與二傳手使用。
我得到了正確的約束嗎?還有其他可以改進的地方嗎?對原始約束是否有任何解釋,或許它是脫離了語境?
另外我想知道這個聲明中複雜的,「不可測試」的部分是否真的有益。因爲我們沒有使用過載,我們可以簡單地依靠正常的模板實例:
template <class String>
void set_name(String &&name) noexcept(
std::is_nothrow_assignable<decltype((name_)), String>::value) {
name_ = std::forward<String>(name);
}
當然還有的在noexcept
是否真正重要的一些爭論,有的說不要擔心太多關於它除了移動/交換原語:
template <class String>
void set_name(String &&name) {
name_ = std::forward<String>(name);
}
也許有了概念,限制模板並不是不合理的困難,只是爲了改進錯誤信息。
template <class String>
requires std::is_assignable<decltype((name_)), String>::value
void set_name(String &&name) {
name_ = std::forward<String>(name);
}
這仍然會對它不能是虛擬的缺點,它必須在頭(雖然希望模塊將最終渲染沒有實際意義),但這似乎相當受教。
嗯,房間裏有人說它的約束不正確,不接受字符串文字 - 也許我們可以cc @HowardHinnant?它也不適合我。 [視頻的談話](http://youtu.be/xnqTKD8uD64?t=1h15m21s) – dyp 2014-10-01 19:26:59
@PiotrS .: MSVC沒有別名,所以我忘了:/ – 2014-10-01 21:10:34
@MooingDuck嘿,VS2013支持別名和他們的stdlib工具C++ 14 type_trait別名。 – bames53 2014-10-01 21:17:21