作者LPH66 (かつて交わした約束)
看板C_and_CPP
標題[心得] 運算子覆載 (was: Re: [問題] 初值陣列...)
時間Wed Apr 5 18:03:28 2017
原 PO 的問題已經離原標題很遠了所以改個標題
而且其實搞懂這邊對最一開始的問題並沒有幫助所以 (ry
: (參考實作可看
: http://en.cppreference.com/w/cpp/types/integral_constant
: 下面 Possible implementation 那裡)
: → hunandy14: 看了好久還是不能理解QQ 容我一個一個問麼 04/03 22:31
: → hunandy14: std::integral_constant 裡面的 04/03 22:33
: → hunandy14: operator value_type() 這是什麼功用算是operator嗎 04/03 22:34
: → LPH66: 那是轉型用, 是在需要一個這種物件實體時才要的 04/04 02:22
: → LPH66: 跟本文的編譯時期常數無關 04/04 02:22
: → LPH66: operator() 也是一樣, 是在需要一個 functor 時才要的 04/04 06:35
: 推 hunandy14: Possible implementation 那裡 提到的 size_t 04/04 17:31
: → hunandy14: 連結到網頁內怎麼沒發現這個~ 04/04 17:32
: → hunandy14: 我也順便去看了 functor 想請問那兩個有什麼差異~ 04/04 17:33
: → hunandy14: operator value_type() || value_type operator()() 04/04 17:34
: → hunandy14: 然後為什麼 op 名稱還可以帶 value_type 字樣~ 04/04 17:35
: → hunandy14: 看到的用法都是直接 operator() 04/04 17:36
你這裡把好幾個長得很像但實際上不太一樣的語法給全部混在一起了
不過雖然實際上不太一樣, 它們還是有個共同點就是都是運算子覆載
一般來說, 運算子覆載的語法是
回傳型態 operator 運算子 (參數) {...}
相信你應該知道 operator + 之類的覆載要怎麼寫, 這裡就不重覆了
這裡要提的是兩種比較特別的運算子覆載
====
1. operator ()
它覆載的運算子就是長成一對小括號
至於什麼運算子是小括號? 就是函數呼叫
也就是說, 這是覆載拿這個物件當做函數名來呼叫時的行為
具有這種特性的物件我們稱做 functor, 或是 function object
中文可以叫做「函式物件」, 一些細節部份就請參照維基百科:
https://zh.wikipedia.org/wiki/%E5%87%BD%E6%95%B0%E5%AF%B9%E8%B1%A1
這裡可以注意到因為 "operator ()" 是被覆載的運算子名稱
所以實際上在定義裡會有兩組小括號併排, 像是上面例子裡的
bool operator()(const int &a, const int &b) const
這種東西的用途有點類似 Strategy pattern
把一些演算法裡的關鍵部份抽出來使其可以自訂
這樣同一套演算法就能用在很多不同地方
如維基百科條目所言, <algorithm> 的眾多演算法依賴於這種東西做處理
<functional> 裡也有定義了許多預先定義的小函數可以使用
====
2. 轉型運算子
是的, 轉型也是一個單元運算子
一般來說, 對於物件型態之間的轉型有兩個可以定義的方向
常見的是目標方定義, 就是在轉向的目標類別裡定義建構子
例如
class Foo
{
public:
Foo(Bar bar) {...} //一個 Bar 物件轉成一個 Foo 物件
};
這裡要提的則是來源方定義, 表示我這物件要轉成這型態時要怎麼辦
它的語法是在運算子覆載的語法裡, 在運算子那個位置擺上目標型態
然後不要寫回傳型態 (因為就一定是目標型態了)
也不要加參數 (轉型就是物件本身轉過去所以不會有參數)
例如
class Bar
{
public:
operator Foo () {...} //一個 Bar 物件轉成一個 Foo 物件
operator int () {...} //一個 Bar 物件轉成一個 int
};
那因為是目標型態所以任何代表一個型態的名字都能放在那裡
像是上例的 Foo 或是 typedef 來的名字如你問的 value_type 等等
====
然後, 還是簡單拉回到 std::integral_constant
template<class T, T v>
struct integral_constant {
static constexpr T value = v;
typedef T value_type;
typedef integral_constant type; // using injected-class-name
constexpr operator value_type() const noexcept { return value; }
constexpr value_type operator()() const noexcept { return value; }
};
value 是值, value_type 和 type 是 typedef 名這個應該不用多說
operator value_type() 定義一個 integral_constant<T> 物件如何轉成 value_type
operator () 定義一個 integral_constant<T> 物件當做函數呼叫時回傳其值
這兩個就是上面提的兩種運算子覆載
不過這兩種都是需要一個這個物件實體才能進行的動作
和前文在討論的編譯時期常數 (用來宣告陣列大小) 是完全無關的
--
1985/01/12 三嶋鳴海 1989/02/22 優希堂悟 1990/02/22 冬川こころ 1993/07/05 小町
つぐみ 歡迎來到 1994/05/21 高江ミュウ 1997/03/24 守野いづみ 1997/03/24 伊野瀬
チサト 1998/06/18 守野くるみ 打越鋼太郎的 1999/10/19 楠田ゆに 2000/02/15 樋口遙
2002/12/17 八神ココ 2011/01/11 HAL18於朱倉岳墜機 ∞與∫的世界 2011/04/02 茜崎空
啟動 2012/05/21 第貮日蝕計畫預定 2017/05/01~07 LeMU崩壞 2019/04/01~07 某大學合宿
--
※ 發信站: 批踢踢實業坊(ptt.cc), 來自: 140.112.30.32
※ 文章網址: https://webptt.com/m.aspx?n=bbs/C_and_CPP/M.1491386611.A.4DA.html
1F:推 hunandy14: 非常感謝~ 04/05 23:16
2F:→ hunandy14: 我理解的套路是~tuple_size繼承了 04/05 23:20
3F:→ hunandy14: integral_constant 並在初始化時透過sizeof...給長度 04/05 23:21
4F:→ hunandy14: 然後因為他有value 使得可以 t_s::v 能夠返回出長度 04/05 23:23
5F:→ hunandy14: 哦 所以你才會說 op 這裡沒影響到我的問題 04/05 23:24
6F:推 hunandy14: 如果我把它實作出一個物件,我要怎麼取得值 04/05 23:34
7F:→ hunandy14: 一樣直接 :: 取得嗎? 像這樣 04/05 23:35
8F:→ hunandy14: integral_constant<int, 2> a; 04/05 23:35
9F:→ hunandy14: cout << a::value << endl; 如此一來就會觸發倒數2行嗎 04/05 23:36
10F:→ hunandy14: operator value_type() 04/05 23:37
11F:→ hunandy14: 測試一下並不是我想得這樣QQ 能夠舉個例子觸發他們嗎~ 04/05 23:57
12F:→ LPH66: 文中都說了, 一個是轉型一個是當函數呼叫 04/06 00:24
13F:→ LPH66: 轉型那個是 (int)a (因為 a 的 value_type 是 int) 04/06 00:24
14F:→ LPH66: 呼叫則是 a() 04/06 00:25
15F:→ LPH66: a::value 就一樣只是取 value 那個成員而已 04/06 00:25
16F:推 ACMANIAC: 推 打越粉! 04/06 05:00
17F:→ hunandy14: 沒想到~ 了解 04/06 12:08