我會以降低複雜性和公用事業3個解決方案。最後的解決方案是最簡單的,也是最簡單的。
小,如果有用的話,元編程庫:
template<class Lhs, class Rhs>
using plus_equal_result = decltype(std::declval<Lhs>()+=std::declval<Rhs>());
template<class Lhs, class Rhs>
using can_plus_equal = can_apply< plus_equal_result, Lhs, Rhs >;
template<class T>
using can_self_plus_equal = can_plus_equal< T&, T const& >;
這給我們返回true類型或假的一些不錯的特點:
template<class...>struct types{using type=types;};
namespace details {
template<template<class...>class Z, class types, class=void>
struct can_apply : std::false_type {};
template<template<class...>class Z, class...Ts>
struct can_apply<Z,types<Ts...>,std::void_t<Z<Ts...>>> :
std::true_type
{};
}
template<template<class...>class Z, class...Ts>
using can_apply = details::can_apply<Z,types<Ts...>>;
一種用於+=
結果特質類型取決於+=
是否有效。
template<class A, class T, bool b = can_self_plus_equal<T>{}>
struct A_maybe_plus_equal {};
template<class A, class T>
struct A_maybe_plus_equal<A, T, true> {
A& self() { return *static_cast<A*>(this); }
A& operator+=(T && t)
{
self().mResult += std::move(t);
return self();
}
template<class U>
std::enable_if_t<can_plus_equal<T&,U>{},A&> operator+=(U && u)
{
self().mResult += std::forward<U>(u);
return self();
}
};
這給我們一個+=
如果我們通過真實。
template <class T>
class A:
public A_maybe_plus_equal<A<T>, T>
{
friend class A_maybe_plus_equal<A<T>, T>;
public:
// nothing needed
private:
T mValue;
};
它給你一個+=
重載需要const T&
或T&&
或在右手邊,當且僅當T& += T const&
一個U&&
是有效的表達式。
這是「完美」的解決方案,但它很複雜。
請注意,每個操作員可以單獨完成,因此您沒有專業化的組合爆炸。
現在,有一個更容易的選項。它的缺點是它不支持右邊的基於{}
的構建,並且在一些標準的閱讀下它是非法的。
它,但是,仍然SFINAE友好:
template <typename T>
class A {
public:
template<class U>
auto operator +=(U&&rhs)
-> decltype((std::declval<T&>()+=std::declval<U&&>()),void(),A&)
// or std::enable_if_t<can_plus_equal<T&,U>{},A&>
{
mValue += std::forward<U>(rhs);
return *this;
}
private:
T mValue;
};
這可以摺疊成以上選項,並給雙方{}
和完善的轉發語法。我發現如果您有template
理想轉發器,則可以刪除T const&
。
這是技術上未定義行爲的原因是標準要求所有模板函數至少有一組參數可以使其主體能夠編譯。在給定的類實例中,上面的模板+=
可能沒有這樣的類型參數集,這使得程序不合格,不需要診斷(即UB)。
還有另一個規則,除非被調用,否則模板類的成員函數不會被實例化。有人認爲這個規則取代了我在最後一段中提到的規則。
另一個說法是,只要模板參數與封閉類以及模板方法本身混合在一起,這種方法就可能是合法的,導致它可實例化。我猜想這是標準委員會的意圖,但我不知道如何閱讀標準才能獲得此結果。
此參數也適用於答案#1中的plus_equal
函數。該實現不需要那麼簡單。另外,#1提供{}
基於+=
的語法,這是使用它的一個實際原因。這個問題 - 這個程序在技術上是不健全的 - 是學術問題,因爲我使用的所有編譯器對這個構造都沒有問題。
這一段的第三段給了我們最後的選擇。沒做什麼。
template <typename T>
class A {
public:
A& operator +=(const T &rhs) {
mValue += rhs;
return *this;
}
private:
T mValue;
};
,這意味着你不能SFINAE測試+=
不工作,但只要你不叫+=
它「作品」。例如,這是vector
的operator<
的工作方式。這是一個較低的「質量」解決方案,標準庫中的這種情況往往會隨着時間的推移而得到修復。
但是,作爲第一遍,最後的選擇通常是最好的。只有當你期望SFINAE的要求是上面的值得。
最終,C++ 1z引入了概念。我相信概念會使這個問題更加容易,因爲根據封閉類的類型參數來消除超載是std
中的一個長期問題。
看起來你正在尋找['enable_if'](http://en.cppreference.com/w/cpp/types/enable_if) – NathanOliver
@PiotrS。甚至沒有想到我急於回答:)但是,在不太可能的情況下,OP希望顯式實例化一個沒有實現'operator +'的類型的模板,上面的代碼將失敗。 – Praetorian
@DanWatkins但是這只是一個通知,你可能希望這個操作符被排除在重載設置之外 –