討論區快速選單
知識庫快速選單
程式設計俱樂部Facebook粉絲團 網路投保旅行平安險
[ 回上頁 ] [ 討論區發言規則 ]
C++ 建構者
更改我的閱讀文章字型大小
作者 : kie(初學者) 人氣指數超過10000點
[ 貼文 115 | 人氣 18287 | 評價 240 | 評價/貼文 2.09 | 送出評價 10 次 ] 
[ 給個讚 ]  [ 給個讚 ]  [ 回應本文 ]  [ 發表新文 ]  [ 回上頁 ] [ 回討論區列表 ] [ 回知識入口 ]
2010/12/5 下午 04:46:08
假設,我們定義了一個 class A。
然後,在程式中寫 A::A() 是代表什麼意思?
最近換了一個 Complier 後,這行程式會出現編譯錯誤,但又不確定前人為何寫這樣的程式。
這樣的寫法有什麼用意嗎?
可否用 A a; 來替代?
作者 : cxxlman(CxxlMan) C++優秀好手貼文超過1000則
[ 貼文 1042 | 人氣 3227 | 評價 1260 | 評價/貼文 1.21 | 送出評價 28 次 ] 
[ 給個讚 ]  [ 給個讚 ]  [ 回應本文 ]  [ 發表新文 ]  [ 回上頁 ] [ 回討論區列表 ] [ 回知識入口 ]
2010/12/5 下午 04:52:59

>假設,我們定義了一個 class A。
>然後,在程式中寫 A::A() 是代表什麼意思?
>最近換了一個 Complier 後,這行程式會出現編譯錯誤,但又不確定前人為何寫這樣的程式。
>這樣的寫法有什麼用意嗎?
>可否用 A a; 來替代?

你題目都已自己回答了
 A::A() 就是建構者

當你用 A a; 建立物件時
就會自動呼叫 A::A()

這是 C++ 佷基礎的部份
找本書看看吧
作者 : kie(初學者) 人氣指數超過10000點
[ 貼文 115 | 人氣 18287 | 評價 240 | 評價/貼文 2.09 | 送出評價 10 次 ] 
[ 給個讚 ]  [ 給個讚 ]  [ 回應本文 ]  [ 發表新文 ]  [ 回上頁 ] [ 回討論區列表 ] [ 回知識入口 ]
2010/12/5 下午 10:10:42

>你題目都已自己回答了
> A::A() 就是建構者
>
>當你用 A a; 建立物件時
>就會自動呼叫 A::A()
>
>這是 C++ 佷基礎的部份
>找本書看看吧

問題不在於他是建構子, 而是為何同樣的程式碼在前舊版的 Complier 會過而新版的卻發生錯誤!
原始程式碼是寫 A::A(); 而不是 A a;
Complier 允許我們寫 A::A(); 嗎?
作者 : kie(初學者) 人氣指數超過10000點
[ 貼文 115 | 人氣 18287 | 評價 240 | 評價/貼文 2.09 | 送出評價 10 次 ] 
[ 給個讚 ]  [ 給個讚 ]  [ 回應本文 ]  [ 發表新文 ]  [ 回上頁 ] [ 回討論區列表 ] [ 回知識入口 ]
2010/12/5 下午 10:24:57
補充一下, 編不過的 Complier 是 IBM XLC 9.0 Complier
作者 : sflam(Raymond)討論區板主 Visual C++ .NET卓越專家VC++一代宗師新手入門優秀好手資訊類作業求救頂尖高手C++一代宗師貼文超過4000則
[ 貼文 4945 | 人氣 9172 | 評價 32290 | 評價/貼文 6.53 | 送出評價 142 次 ] 
[ 給個讚 ]  [ 給個讚 ]  [ 回應本文 ]  [ 發表新文 ]  [ 回上頁 ] [ 回討論區列表 ] [ 回知識入口 ]
2010/12/6 上午 12:06:44
樓主, 這樣子問問題是很難有結果的. 程式上的問題應該附上完整程式碼.

你應該做的是:
  1. 把完整程式貼出來, 或是把程式刪減至最簡單而又能夠在編譯時顯示原來的問題.
  2. 同時貼出編譯上面程式時, 完整的編譯器錯誤訊息,
  3. 指出該錯誤訊息所標識的句子.
作者 : kie(初學者) 人氣指數超過10000點
[ 貼文 115 | 人氣 18287 | 評價 240 | 評價/貼文 2.09 | 送出評價 10 次 ] 
[ 給個讚 ]  [ 給個讚 ]  [ 回應本文 ]  [ 發表新文 ]  [ 回上頁 ] [ 回討論區列表 ] [ 回知識入口 ]
2010/12/6 上午 11:11:13
<< 範例程式 >>
#include <iostream>

using namespace std;

class MyClass{

public:

MyClass(){
cout << "constructor MyClass" << endl;
}

};

int main(){

    MyClass::MyClass();
    return 0;
}

<Complier 錯誤訊息>>
"test.cpp", line 17.5: 1540-0144 (S) A non-local declaration is not allowed in a function body.

作者 : ma_hty(白老鼠(Gary))討論區板主 OpenGL卓越專家DirectX優秀好手C++頂尖高手貼文超過2000則人氣指數超過70000點
[ 貼文 2189 | 人氣 89850 | 評價 10120 | 評價/貼文 4.62 | 送出評價 79 次 ] 
[ 給個讚 ]  [ 給個讚 ]  [ 回應本文 ]  [ 發表新文 ]  [ 回上頁 ] [ 回討論區列表 ] [ 回知識入口 ]
2010/12/6 上午 11:42:36
大概是你說的編譯器 "IBM XLC 9.0 Complier"
有點與別不同了...

雖然 建構物件然後馬上捨棄 是 多餘的行動,
但是 編譯器 一般是不會把它當作 編譯錯誤 的.
作者 : sflam(Raymond)討論區板主 Visual C++ .NET卓越專家VC++一代宗師新手入門優秀好手資訊類作業求救頂尖高手C++一代宗師貼文超過4000則
[ 貼文 4945 | 人氣 9172 | 評價 32290 | 評價/貼文 6.53 | 送出評價 142 次 ] 
[ 給個讚 ]  [ 給個讚 ]  [ 回應本文 ]  [ 發表新文 ]  [ 回上頁 ] [ 回討論區列表 ] [ 回知識入口 ]
2010/12/6 下午 12:05:29
MyClass::MyClass() 是預設建構子 (default constructor) 的 function signature, 是讓你在 class 外定義成員的語法.

〔.h 檔〕
  class Myclass
  {
    MyClass(); // 這是宣告
  };
  
〔.cpp 檔〕
  // 這是定義
  MyClass::MyClass()
  {
    cout << "constructor MyClass" << endl;
  }

Myclass 是個類型, 你要用它來定義物件, 而不是呼叫 MyClass::MyClass():
  int main()
  {
    Myclass myobj;
    ...
  }

直接呼叫 MyClass::MyClass() 是錯誤的語法, 我懷疑你於你上面所說, 舊版的可以編譯的是上面你所貼的程式.

作者 : sflam(Raymond)討論區板主 Visual C++ .NET卓越專家VC++一代宗師新手入門優秀好手資訊類作業求救頂尖高手C++一代宗師貼文超過4000則
[ 貼文 4945 | 人氣 9172 | 評價 32290 | 評價/貼文 6.53 | 送出評價 142 次 ] 
[ 給個讚 ]  [ 給個讚 ]  [ 回應本文 ]  [ 發表新文 ]  [ 回上頁 ] [ 回討論區列表 ] [ 回知識入口 ]
2010/12/6 下午 12:36:12
對不起, 剛剛查看了一下, MyClass::MyClass() 是可以的, 它的作用是呼叫預設建構子. 因為如果你這樣寫的話:
   MyClass();

它不是呼叫, 編譯器會把它視為函式宣告.

我之前的回覆是錯誤的. 為此致歉.
作者 : cxxlman(CxxlMan) C++優秀好手貼文超過1000則
[ 貼文 1042 | 人氣 3227 | 評價 1260 | 評價/貼文 1.21 | 送出評價 28 次 ] 
[ 給個讚 ]  [ 給個讚 ]  [ 回應本文 ]  [ 發表新文 ]  [ 回上頁 ] [ 回討論區列表 ] [ 回知識入口 ]
2010/12/6 下午 02:50:50

>對不起, 剛剛查看了一下, MyClass::MyClass() 是可以的, 它的作用是呼叫預設建構子. 因為如果你這樣寫的話:
>   MyClass();
>
>它不是呼叫, 編譯器會把它視為函式宣告.

我用 Dev C++ 試了一下, 不管是用 MyClass() 還是用 MyClass::MyClass() 都能正確呼叫 預設建構子, 表面上這樣用好像把 預設建構子 當一般函數在用, 但當他結束時還是會再呼叫 解構子, 要注意這點

也會確實建立一個看不見的物件, 你可以用 return MyClass() 或 return MyClass::MyClass() 傳回這物件

順便舉例一下 auto_ptr 利用這自動呼叫 解構子 的特性來解決沒法 delete 的用法

  char *Buf = new char[100];
  auto_ptr<char> n(*Buf); // 因 return 前沒辦法呼叫 delete [] Buf
  return string(Buf);



 

作者 : sflam(Raymond)討論區板主 Visual C++ .NET卓越專家VC++一代宗師新手入門優秀好手資訊類作業求救頂尖高手C++一代宗師貼文超過4000則
[ 貼文 4945 | 人氣 9172 | 評價 32290 | 評價/貼文 6.53 | 送出評價 142 次 ] 
[ 給個讚 ]  [ 給個讚 ]  [ 回應本文 ]  [ 發表新文 ]  [ 回上頁 ] [ 回討論區列表 ] [ 回知識入口 ]
2010/12/8 上午 04:53:53
再仔細的研究樓主的程式及語言標準文件, 在 12.1 Constructors 的第 2 段裡:

"A constructor is used to initialize objects of its class type. Because constructors do not have names, they are never found during name lookup; however an explicit type conversion using the functional notation (5.2.3) will cause a constructor to be called to initialize an object."

從這個來看, 句子:
  MyClass::MyClass();

在語法上是錯誤的.

正確的語法是
  MyClass();

但它的作用就像 CxxlMan 大大所講的一樣, 建構一個無名的物件, 然後又會解構.

作者 : chiuinan2(青衫)討論區板主 Visual C++ .NET卓越專家VC++一代宗師Visual Basic優秀好手資訊類作業求救卓越專家一般曠世奇才程式設計甘苦談優秀好手C++ Builder優秀好手上班族的哈拉園地優秀好手C++頂尖高手Assembly優秀好手貼文超過3000則人氣指數超過150000點
[ 貼文 3732 | 人氣 170106 | 評價 34520 | 評價/貼文 9.25 | 送出評價 125 次 ] 
[ 給個讚 ]  [ 給個讚 ]  [ 回應本文 ]  [ 發表新文 ]  [ 回上頁 ] [ 回討論區列表 ] [ 回知識入口 ]
2010/12/8 上午 05:54:49
從最初個人的感覺是: 無意義且不妥的寫法, 最好了解後改掉.

實際意義, 我也是從Raymond最後的結論才了解到.

原始程式都沒人註解嗎? 也沒任何文件描述該物件的目的與用途嗎?

如果沒有, 就從自己做起吧. 該有的文件, 該有的註解, 務必一一寫下來. 為自己好, 也為將來的人好. -> 壞習慣一旦養成後, 就很難改了. 切記, 切記!
作者 : cxxlman(CxxlMan) C++優秀好手貼文超過1000則
[ 貼文 1042 | 人氣 3227 | 評價 1260 | 評價/貼文 1.21 | 送出評價 28 次 ] 
[ 給個讚 ]  [ 給個讚 ]  [ 回應本文 ]  [ 發表新文 ]  [ 回上頁 ] [ 回討論區列表 ] [ 回知識入口 ]
2010/12/8 下午 07:46:26

>從這個來看, 句子:
>  MyClass::MyClass();
>
>在語法上是錯誤的.
>
>正確的語法是
>  MyClass();

MyClass(); // 把這看成是建立 Myclass 物件,順便呼叫 MyClass()

MyClass::MyClass(); // 把這看成呼叫 MyClass(),順便先立 Myclass 物件

R大 以上你看如何
作者 : cxxlman(CxxlMan) C++優秀好手貼文超過1000則
[ 貼文 1042 | 人氣 3227 | 評價 1260 | 評價/貼文 1.21 | 送出評價 28 次 ] 
[ 給個讚 ]  [ 給個讚 ]  [ 回應本文 ]  [ 發表新文 ]  [ 回上頁 ] [ 回討論區列表 ] [ 回知識入口 ]
2010/12/8 下午 08:07:53
還有一些有趣的用法

class MyClass
{
public:

  MyClass()
  {
    cout << "constructor MyClass" << endl;
  }

  MyClass(MyClass*)
  {
    cout << "copy constructor MyClass" << endl;
  }


  MyClass(int i)
  {
    cout << "constructor Myclass i = " << i << endl;
  }
  
  ~MyClass()
  {
    cout << "Destructor MyClass" << endl;
  }
  
  void F()
  {
    cout << "F()" << endl;
  }
};


int main(int argc, char *argv[])
{
    MyClass().F();
    
    MyClass(new MyClass);
    
    Myclass *O = new MyClass;
    // Myclass (O); // 奇怪這樣不行
    Myclass n(O); // 只能這樣
    
    
    system("PAUSE");
    return EXIT_SUCCESS;
}
作者 : sflam(Raymond)討論區板主 Visual C++ .NET卓越專家VC++一代宗師新手入門優秀好手資訊類作業求救頂尖高手C++一代宗師貼文超過4000則
[ 貼文 4945 | 人氣 9172 | 評價 32290 | 評價/貼文 6.53 | 送出評價 142 次 ] 
[ 給個讚 ]  [ 給個讚 ]  [ 回應本文 ]  [ 發表新文 ]  [ 回上頁 ] [ 回討論區列表 ] [ 回知識入口 ]
2010/12/9 上午 12:08:42

>>從這個來看, 句子:
>>  MyClass::MyClass();
>>
>>在語法上是錯誤的.
>>
>>正確的語法是
>>  MyClass();
>
>MyClass(); // 把這看成是建立 Myclass 物件,順便呼叫 MyClass()

這是一個 temporary 的物件, 建構子會自動的 invoke, 然後又會解構. 獨自使用沒有太大意義 (除非呼叫它有 side effect, 而你只想要它的 side effect). 其中一個用法是做為函式的參數:

  class Myclass { ... };

  void foo(const MyClass& obj)
  {
    ...
  }

  int main(void)
  {
    foo(MyClass());
  }


>MyClass::MyClass(); // 把這看成呼叫 MyClass(),順便先立 Myclass 物件

前面的 MyClass:: 是個 scope qualification, 但 C++ 標準 12.1p2 否定這個用法.

而且 C++ 語言沒有「呼叫建構子來建立物件」的說法及概念. 一定是先有足夠大小的空間, 然後才透過建構子來初始化空間的內容, 讓空間變成一個完整的物件.


>還有一些有趣的用法
>
>class MyClass
>{
>public:
>
> MyClass()
> {
> cout << "constructor MyClass" << endl;
> }
>
> MyClass(MyClass*)

這個不是 copy constructor, copy constructor 的參數一定是個 reference.

> {
> cout << "copy constructor MyClass" << endl;
> }

〔略〕

>int main(int argc, char *argv[])
>{
> MyClass().F();
>
> MyClass(new MyClass);
>
> Myclass *O = new MyClass;
> // Myclass (O); // 奇怪這樣不行

這是個定義的語法. 跟這個比較:
  int (i);

C++ 的 declarator 的語法:
  http://csci.csusb.edu/dick/c++std/syntax.html#Declarations

上面的不行是因為 'O' 已經在前一行定義了. 用一個新的名字就可以了:
  Myclass (x);


作者 : cxxlman(CxxlMan) C++優秀好手貼文超過1000則
[ 貼文 1042 | 人氣 3227 | 評價 1260 | 評價/貼文 1.21 | 送出評價 28 次 ] 
[ 給個讚 ]  [ 給個讚 ]  [ 回應本文 ]  [ 發表新文 ]  [ 回上頁 ] [ 回討論區列表 ] [ 回知識入口 ]
2010/12/9 上午 02:52:40

>這是一個 temporary 的物件, 建構子會自動的 invoke, 然後又會解構. 獨自使用沒有太大意義 (除非呼叫它有 side effect, 而你只想要它的 side effect). 其中一個用法是做為函式的參數:

還有前面我舉的 auto_ptr 的用法也是在利用他的 side effect



>> MyClass::MyClass(); // 把這看成呼叫 MyClass(),順便先立 Myclass 物件
>
>前面的 MyClass:: 是個 scope qualification, 但 C++ 標準 12.1p2 否定這個用法.

我試出來了, 若再多定義一個如下的函數, 那麼 MyClass:: 就派上用場了

void MyClass()
{
    cout << "這是一般函數" << endl;
}


    
>> Myclass *O = new MyClass;
>> // Myclass (O); // 奇怪這樣不行
>
>這是個定義的語法. 跟這個比較:
>  int (i);
>
>C++ 的 declarator 的語法:
>  http://csci.csusb.edu/dick/c++std/syntax.html#Declarations
>
>上面的不行是因為 'O' 已經在前一行定義了. 用一個新的名字就可以了:
>  Myclass (x);

嗯, 了解了, 我把他改成如下, 就順利過關了
Myclass ((MyClass*)O)
作者 : ma_hty(白老鼠(Gary))討論區板主 OpenGL卓越專家DirectX優秀好手C++頂尖高手貼文超過2000則人氣指數超過70000點
[ 貼文 2189 | 人氣 89850 | 評價 10120 | 評價/貼文 4.62 | 送出評價 79 次 ] 
[ 給個讚 ]  [ 給個讚 ]  [ 回應本文 ]  [ 發表新文 ]  [ 回上頁 ] [ 回討論區列表 ] [ 回知識入口 ]
2010/12/9 上午 04:41:38
>從這個來看, 句子:
>  MyClass::MyClass();
>
>在語法上是錯誤的.
>
>正確的語法是
>  MyClass();
>
>但它的作用就像 CxxlMan 大大所講的一樣, 建構一個無名的物件, 然後又會解構.
>
>

呼叫
  MyClass::MyClass();
的意思是 以 default constructor 建構無名物件後馬上解構.
它的意思是明確的, 語法也是正確的, 為什麼你會說這是語法錯誤呢?
你的實在的意思是否 "它是不能被用作 default constructor 的 declaration" 呢?
作者 : cxxlman(CxxlMan) C++優秀好手貼文超過1000則
[ 貼文 1042 | 人氣 3227 | 評價 1260 | 評價/貼文 1.21 | 送出評價 28 次 ] 
[ 給個讚 ]  [ 給個讚 ]  [ 回應本文 ]  [ 發表新文 ]  [ 回上頁 ] [ 回討論區列表 ] [ 回知識入口 ]
2010/12/9 下午 01:26:10

>>從這個來看, 句子:
>>  MyClass::MyClass();
>>
>>在語法上是錯誤的.
>>
>>正確的語法是
>>  MyClass();
>>
>>但它的作用就像 CxxlMan 大大所講的一樣, 建構一個無名的物件, 然後又會解構.
>>
>>
>
>呼叫
> MyClass::MyClass();
>的意思是 以 default constructor 建構無名物件後馬上解構.
>它的意思是明確的, 語法也是正確的, 為什麼你會說這是語法錯誤呢?
>你的實在的意思是否 '它是不能被用作 default constructor 的 declaration' 呢?
>

經實測

MyClass::MyClass(); // 正確
MyClass::F(); // 一般函數不行,但錯誤的原因是沒有實體物件

Myclass *O = new MyClass;
O->F(); // 正確
O->MyClass::F(); // 正確
作者 : cxxlman(CxxlMan) C++優秀好手貼文超過1000則
[ 貼文 1042 | 人氣 3227 | 評價 1260 | 評價/貼文 1.21 | 送出評價 28 次 ] 
[ 給個讚 ]  [ 給個讚 ]  [ 回應本文 ]  [ 發表新文 ]  [ 回上頁 ] [ 回討論區列表 ] [ 回知識入口 ]
2010/12/9 下午 01:36:18

>
>呼叫
> MyClass::MyClass();
>的意思是 以 default constructor 建構無名物件後馬上解構.
>它的意思是明確的, 語法也是正確的, 為什麼你會說這是語法錯誤呢?
>你的實在的意思是否 '它是不能被用作 default constructor 的 declaration' 呢?
>

經實測

MyClass::MyClass(); // 正確
MyClass::F(); // 一般成員函數不行,但錯誤的原因是沒有實體物件

Myclass *O = new MyClass;
O->F(); // 正確
O->MyClass::F(); // 正確

還有不是馬上解構,是離開生存簳圍才會解構

main()
{
  MyClass(); // 第一個建構

  if(...)
  {
     MyClass(); // 第二個建構
  } // 第二個解構

  
} // 第一個解構

作者 : ma_hty(白老鼠(Gary))討論區板主 OpenGL卓越專家DirectX優秀好手C++頂尖高手貼文超過2000則人氣指數超過70000點
[ 貼文 2189 | 人氣 89850 | 評價 10120 | 評價/貼文 4.62 | 送出評價 79 次 ] 
[ 給個讚 ]  [ 給個讚 ]  [ 回應本文 ]  [ 發表新文 ]  [ 回上頁 ] [ 回討論區列表 ] [ 回知識入口 ]
2010/12/9 下午 02:44:59
>MyClass::MyClass(); // 正確
>...
>還有不是馬上解構,是離開生存簳圍才會解構
>

在 VS6, 這樣生成的物件在該指令完結時, 就會被解構的.
請問你是用哪一個 編譯器 呢?
作者 : cxxlman(CxxlMan) C++優秀好手貼文超過1000則
[ 貼文 1042 | 人氣 3227 | 評價 1260 | 評價/貼文 1.21 | 送出評價 28 次 ] 
[ 給個讚 ]  [ 給個讚 ]  [ 回應本文 ]  [ 發表新文 ]  [ 回上頁 ] [ 回討論區列表 ] [ 回知識入口 ]
2010/12/9 下午 03:55:09

>>MyClass::MyClass(); // 正確
>>...
>>還有不是馬上解構,是離開生存簳圍才會解構
>>
>
>在 VS6, 這樣生成的物件在該指令完結時, 就會被解構的.
>請問你是用哪一個 編譯器 呢?
>

經實測正如你說的馬上解構, 還真短命

class MyClass
{
public:

  MyClass()
  {
    cout << "Constructor MyClass" << endl;
  }

  ~MyClass()
  {
    cout << "Destructor MyClass" << endl;
  }
  
  void AreYouOK()
  {
    cout << "我還活著" << endl;
  }
};

class autoFree
{
  Myclass *O;
public:
  autoFree(Myclass *o)
  {
    O = o;
  }
  ~autoFree()
  {
    delete O;
  }
};

int main(int argc, char *argv[])
{
    Myclass *O = new MyClass;
    autoFree((Myclass *)O); // 想活長點就加個名稱吧 autoFree autoFree_Obj(O);
    O->AreYouOK();
    
    system("PAUSE");
    return EXIT_SUCCESS;
}
作者 : ma_hty(白老鼠(Gary))討論區板主 OpenGL卓越專家DirectX優秀好手C++頂尖高手貼文超過2000則人氣指數超過70000點
[ 貼文 2189 | 人氣 89850 | 評價 10120 | 評價/貼文 4.62 | 送出評價 79 次 ] 
[ 給個讚 ]  [ 給個讚 ]  [ 回應本文 ]  [ 發表新文 ]  [ 回上頁 ] [ 回討論區列表 ] [ 回知識入口 ]
2010/12/9 下午 04:54:51
> ...
>int main(int argc, char *argv[])
>{
> Myclass *O = new MyClass;
> autoFree((Myclass *)O); // 想活長點就加個名稱吧 autoFree autoFree_Obj(O);
> O->AreYouOK();
>
>...

(@@?? 好奇怪的程式... 它是想幹啥呢? 耐人尋味... 耐人尋味... )

new operator 生成的物件, 除非你特地用 delete operator 去解構它,
不然, 在程式完結之前, 是不會被解構的. 你是不需做額外的行動去保證這件事的.
作者 : ma_hty(白老鼠(Gary))討論區板主 OpenGL卓越專家DirectX優秀好手C++頂尖高手貼文超過2000則人氣指數超過70000點
[ 貼文 2189 | 人氣 89850 | 評價 10120 | 評價/貼文 4.62 | 送出評價 79 次 ] 
[ 給個讚 ]  [ 給個讚 ]  [ 回應本文 ]  [ 發表新文 ]  [ 回上頁 ] [ 回討論區列表 ] [ 回知識入口 ]
2010/12/9 下午 04:59:56
>> ...
>>int main(int argc, char *argv[])
>>{
>> Myclass *O = new MyClass;
>> autoFree((Myclass *)O); // 想活長點就加個名稱吧 autoFree autoFree_Obj(O);
>> O->AreYouOK();
>>
>>...
>
>(@@?? 好奇怪的程式... 它是想幹啥呢? 耐人尋味... 耐人尋味... )
>
>用 new operator 生成的物件, 除非你特地用 delete operator 去解構它,
>不然, 在程式完結之前, 是不會被解構的. 你是不需做額外的行動去保證這件事的.
>

更正!!
new operator 生成的物件, 除非你特地用 delete operator 去解構它,
否則不會被解構的. 你是不需做額外的行動去保證這件事的.
作者 : cxxlman(CxxlMan) C++優秀好手貼文超過1000則
[ 貼文 1042 | 人氣 3227 | 評價 1260 | 評價/貼文 1.21 | 送出評價 28 次 ] 
[ 給個讚 ]  [ 給個讚 ]  [ 回應本文 ]  [ 發表新文 ]  [ 回上頁 ] [ 回討論區列表 ] [ 回知識入口 ]
2010/12/9 下午 06:41:23
這次的重點是在 class autoFree, 抱歉沒講清楚, 那是 Smart Pointer 的用法

果然 autoFree 用無名建構出來的物件會馬上又解構了
作者 : sunyear(coco) VC++卓越專家C++頂尖高手貼文超過2000則
[ 貼文 2421 | 人氣 1485 | 評價 6060 | 評價/貼文 2.5 | 送出評價 5 次 ] 
[ 給個讚 ]  [ 給個讚 ]  [ 回應本文 ]  [ 發表新文 ]  [ 回上頁 ] [ 回討論區列表 ] [ 回知識入口 ]
2010/12/9 下午 07:01:29
CMyClass::CMyClass(); 就是CMyClass();
CMyClass::只是多加一個scope,雖然有點脫褲子放屁,但只要不妨礙風化,也不能說它錯。
CMyClass() 就是產生一個CMycLASS的暫態物件。
至於生存期,就和一般的暫態物件一樣啊!(如int()一樣,沒用了就解構)

我的直覺就是這樣,談了好多學理,越看越不懂。
可能我比較不重學理(懶得讀書吧)。
作者 : cxxlman(CxxlMan) C++優秀好手貼文超過1000則
[ 貼文 1042 | 人氣 3227 | 評價 1260 | 評價/貼文 1.21 | 送出評價 28 次 ] 
[ 給個讚 ]  [ 給個讚 ]  [ 回應本文 ]  [ 發表新文 ]  [ 回上頁 ] [ 回討論區列表 ] [ 回知識入口 ]
2010/12/9 下午 07:49:25
int(5.0) = (int)5.0

MyClass(MyClass2_Obj) = (MyClass)MyClass2_Obj

可以這樣相對應看待嗎
作者 : cxxlman(CxxlMan) C++優秀好手貼文超過1000則
[ 貼文 1042 | 人氣 3227 | 評價 1260 | 評價/貼文 1.21 | 送出評價 28 次 ] 
[ 給個讚 ]  [ 給個讚 ]  [ 回應本文 ]  [ 發表新文 ]  [ 回上頁 ] [ 回討論區列表 ] [ 回知識入口 ]
2010/12/9 下午 10:13:58
coco 說得對
簡單看待

MyClass() 可以看成 int() 就好, 暫態物件 用過就沒了

MyClass::MyClass() 就是 Myclass 命名空間堛 MyClass() 暫態物件

作者 : sflam(Raymond)討論區板主 Visual C++ .NET卓越專家VC++一代宗師新手入門優秀好手資訊類作業求救頂尖高手C++一代宗師貼文超過4000則
[ 貼文 4945 | 人氣 9172 | 評價 32290 | 評價/貼文 6.53 | 送出評價 142 次 ] 
[ 給個讚 ]  [ 給個讚 ]  [ 回應本文 ]  [ 發表新文 ]  [ 回上頁 ] [ 回討論區列表 ] [ 回知識入口 ]
2010/12/9 下午 10:58:29
>呼叫
> MyClass::MyClass();
>的意思是 以 default constructor 建構無名物件後馬上解構.

那用:
  MyClass();

就好了.

>它的意思是明確的, 語法也是正確的, 為什麼你會說這是語法錯誤呢?
>你的實在的意思是否 '它是不能被用作 default constructor 的 declaration' 呢?

因為建構子沒有名字 (語言標準 12.1p2) , 所以 '::' 右邊的 "MyClass() 是不能被找到的.

如果可以的話, 那這個應該也是可以的:
  Myclass obj;
  obj.MyClass();

但它是編譯錯誤.

有些編譯器允許:
  obj.MyClass::MyClass();

但跟 『MyClass::MyClass();』 一樣, 都是非標準的行為.

作者 : cxxlman(CxxlMan) C++優秀好手貼文超過1000則
[ 貼文 1042 | 人氣 3227 | 評價 1260 | 評價/貼文 1.21 | 送出評價 28 次 ] 
[ 給個讚 ]  [ 給個讚 ]  [ 回應本文 ]  [ 發表新文 ]  [ 回上頁 ] [ 回討論區列表 ] [ 回知識入口 ]
2010/12/10 上午 12:23:46

>>呼叫
>> MyClass::MyClass();
>>的意思是 以 default constructor 建構無名物件後馬上解構.
>
>那用:
>  MyClass();
>
>就好了.

多加一個叫 MyClass() 的一般函數,就會發現用 MyClass(); 編譯器沒法判斷了, MyClass::MyClass(); 有其必要性

>>它的意思是明確的, 語法也是正確的, 為什麼你會說這是語法錯誤呢?
>>你的實在的意思是否 '它是不能被用作 default constructor 的 declaration' 呢?
>
>因為建構子沒有名字 (語言標準 12.1p2) , 所以 '::' 右邊的 'MyClass() 是不能被找到的.
>
>如果可以的話, 那這個應該也是可以的:
>  Myclass obj;
>  obj.MyClass();

你把 MyClass() 當函數看待了, 應把它當形別看才對, 就像 int() 一樣

MyClass::MyClass(); // MyClass:: 是命名空間, MyClass() 是形別

>
>但它是編譯錯誤.
>
>有些編譯器允許:
>  obj.MyClass::MyClass();
>
>但跟 『MyClass::MyClass();』 一樣, 都是非標準的行為.

obj.MyClass::MyClass(); // 這堛 MyClass() 應視為函數呼叫, 但 MyClass() 若是內定建構子的話也是不能這樣用

obj.MyClass::F(); // 這是正確的, 主要用在父子類別都有同名函數 F(), 可用 MyClass:: 做區分

作者 : sunyear(coco) VC++卓越專家C++頂尖高手貼文超過2000則
[ 貼文 2421 | 人氣 1485 | 評價 6060 | 評價/貼文 2.5 | 送出評價 5 次 ] 
[ 給個讚 ]  [ 給個讚 ]  [ 回應本文 ]  [ 發表新文 ]  [ 回上頁 ] [ 回討論區列表 ] [ 回知識入口 ]
2010/12/10 上午 11:40:31
>如果可以的話, 那這個應該也是可以的:
>  Myclass obj;
>  obj.MyClass();
>但它是編譯錯誤.
>有些編譯器允許:
>  obj.MyClass::MyClass();

呼叫MyClass()會和和建構MyClass物件的MyClass()產生 ambigous。
所以要呼叫建構子要加上 MyClass:: 明白指定後面跟著的是MyClass的成員。
若要呼叫解構子,因為沒有ambigous的問題,所以可以這樣呼叫:
obj.~MyClass();


>但跟 『MyClass::MyClass();』 一樣, 都是非標準的行為.
我不知道"手動"呼叫建構和解構是不是標準,但在一些template container
會需用到"手動"呼叫建構和解構。所以一般C++編譯器應該是可以這樣做的。
但要審慎為之,戒之,慎之...
作者 : cxxlman(CxxlMan) C++優秀好手貼文超過1000則
[ 貼文 1042 | 人氣 3227 | 評價 1260 | 評價/貼文 1.21 | 送出評價 28 次 ] 
[ 給個讚 ]  [ 給個讚 ]  [ 回應本文 ]  [ 發表新文 ]  [ 回上頁 ] [ 回討論區列表 ] [ 回知識入口 ]
2010/12/10 下午 01:53:40
建構子 和 解構子 沒法直接呼叫吧
你從哪看到的
作者 : sunyear(coco) VC++卓越專家C++頂尖高手貼文超過2000則
[ 貼文 2421 | 人氣 1485 | 評價 6060 | 評價/貼文 2.5 | 送出評價 5 次 ] 
[ 給個讚 ]  [ 給個讚 ]  [ 回應本文 ]  [ 發表新文 ]  [ 回上頁 ] [ 回討論區列表 ] [ 回知識入口 ]
2010/12/10 下午 02:30:10
>建構子 和 解構子 沒法直接呼叫吧
>你從哪看到的
在你的編譯器上試試,不能呼叫嗎?
我說過,我不知道它是不是標準。但在一些container,會有這種需求。
例如 MFC 為了強化陣列容器的效率,有自行(手動)呼叫建構子和解構子的程式
以下抄自 MFC STREX.CPP 自行呼叫解構子的例子:
void AFXAPI DestructElements(CString* pElements, int nCount)
{
ASSERT(nCount == 0 ||
AfxIsValidAddress(pElements, nCount * sizeof(CString)));

for (; nCount--; ++pElements)
pElements->~CString();
}
作者 : sunyear(coco) VC++卓越專家C++頂尖高手貼文超過2000則
[ 貼文 2421 | 人氣 1485 | 評價 6060 | 評價/貼文 2.5 | 送出評價 5 次 ] 
[ 給個讚 ]  [ 給個讚 ]  [ 回應本文 ]  [ 發表新文 ]  [ 回上頁 ] [ 回討論區列表 ] [ 回知識入口 ]
2010/12/10 下午 03:39:45
不談例子,我還是喜歡從實際需要來討論。
什麼情況會需要用到自行呼叫建構和解構?
當你需要自行管理物件所在記憶體時,就需要自行呼叫建構和解構。
(很多需要效率考量的情況或不知type的模板沒辦法使用重載 new解決)
既然有需要,C++就應該要提供辦法。
對一個 class CMyclass 的物件 MyObj,
我還是不知道 MyObj.CMyClass::CMyClass();是不是標準,但總看起來礙眼。
C++是用 placement new 來直接呼叫建構子的:
CMyclass *pMyObj= (CMyclass *)malloc(sizeof(CMyclass ));
new(pMyObj) CMyClass(...);//<--相當於 pMyObj->CMyClass::CMyClass(...)
這個new和我們熟知的 new operator 是不一樣的,它不配置記憶體給建構的物件。
而是假設pMyObj是一個已配置該型態的容器,使用它當this來呼叫(複製)建構子。
這種建構物件的方式被大量使用在STL中,這也是為什麼當你嘗試使用new operator
來寫自己的STL時,效率上總比不過正版的STL。

當一個物件使用 placement new 來創建時,就不能用delete來摧毁它。
(因為物件不是真的被new出來的)
唯一的方法就是怎麼來怎麼去,自行呼叫建構子,那就自行呼叫解構子結束物件:
pMyObj->~CMyClass();
free(pMyObj);
因為我不知道有placement delete 所以就假設pMyObj->~CMyClass();是標準語法吧!
(有點霸道,呵呵...)
作者 : sunyear(coco) VC++卓越專家C++頂尖高手貼文超過2000則
[ 貼文 2421 | 人氣 1485 | 評價 6060 | 評價/貼文 2.5 | 送出評價 5 次 ] 
[ 給個讚 ]  [ 給個讚 ]  [ 回應本文 ]  [ 發表新文 ]  [ 回上頁 ] [ 回討論區列表 ] [ 回知識入口 ]
2010/12/10 下午 03:45:48
還是找一個 MFC 使用placement new的例子吧,免得我是自說自話 ^^
AFXTEMPL.H:
template<class TYPE>
AFX_inline void AFXAPI ConstructElements(TYPE* pElements, int nCount)
{
ASSERT(nCount == 0 ||
AfxIsValidAddress(pElements, nCount * sizeof(TYPE)));

// first do bit-wise zero initialization
memset((void*)pElements, 0, nCount * sizeof(TYPE));

// then call the constructor(s)
for (; nCount--; pElements++)
::new((void*)pElements) TYPE;//<--placement new
}
作者 : cxxlman(CxxlMan) C++優秀好手貼文超過1000則
[ 貼文 1042 | 人氣 3227 | 評價 1260 | 評價/貼文 1.21 | 送出評價 28 次 ] 
[ 給個讚 ]  [ 給個讚 ]  [ 回應本文 ]  [ 發表新文 ]  [ 回上頁 ] [ 回討論區列表 ] [ 回知識入口 ]
2010/12/10 下午 05:34:15
實測結果,不管是不是 template class,建構函數是不能被呼叫的,解構函數可以

建構一個物件的流程,(1)配置記憶體 (2)由父類別至子類別一一呼叫建構函數
解構一個物件的流程,(1)由子類別往父類別一一呼叫解構函數 (2)釋放其配置的記憶體

建構函數不能被呼叫原因是因會造成 Memory Leak,因一個物件被建立時一切都假設一無所有,因此配置資料前都不會去釋放原有的資料

解構函數比較無所謂,重複呼叫若有錯誤也比較能被查覺

>new(pMyObj) CMyClass(...);//<--相當於 pMyObj->CMyClass::CMyClass(...)

CMyClass(...) 在這應被看做是形別,相當於 int(5);
new(pMyObj) 這個若是 ::new 指令,它內定會用 pMyObj 所指的記憶體當作要配置的記憶體


>當一個物件使用 placement new 來創建時,就不能用delete來摧毁它。
>(因為物件不是真的被new出來的)
>唯一的方法就是怎麼來怎麼去,自行呼叫建構子,那就自行呼叫解構子結束物件:
>pMyObj->~CMyClass();
>free(pMyObj);
>因為我不知道有placement delete 所以就假設pMyObj->~CMyClass();是標準語法吧!
>(有點霸道,呵呵...)

這點同意, new(pMyObj) 用的是別人配置出來的記憶體,當然不能由自己做釋放,但物件結束還是得呼叫解構函數,難怪解構函數可被直接呼叫

作者 : sunyear(coco) VC++卓越專家C++頂尖高手貼文超過2000則
[ 貼文 2421 | 人氣 1485 | 評價 6060 | 評價/貼文 2.5 | 送出評價 5 次 ] 
[ 給個讚 ]  [ 給個讚 ]  [ 回應本文 ]  [ 發表新文 ]  [ 回上頁 ] [ 回討論區列表 ] [ 回知識入口 ]
2010/12/10 下午 05:51:36
>實測結果,不管是不是 template class,建構函數是不能被呼叫的,解構函數可以
你是用那一款C++?
我用VC6.0是可以呼叫啊(承認VC是不太標準^^)
pMyObj->CMyClass(...);//<--這樣呼叫一定不行(理由前面說了)
pMyObj->CMyClass::CMyClass(...);//<--這樣才可以



>>new(pMyObj) CMyClass(...);//<--相當於 pMyObj->CMyClass::CMyClass(...)
>CMyClass(...) 在這應被看做是形別,相當於 int(5);
不不,pMyObj->CMyClass::CMyClass(...)沒有創建新的暫態物件。
你可以debug進建構子,看this就知是pMyObj沒有錯


作者 : sunyear(coco) VC++卓越專家C++頂尖高手貼文超過2000則
[ 貼文 2421 | 人氣 1485 | 評價 6060 | 評價/貼文 2.5 | 送出評價 5 次 ] 
[ 給個讚 ]  [ 給個讚 ]  [ 回應本文 ]  [ 發表新文 ]  [ 回上頁 ] [ 回討論區列表 ] [ 回知識入口 ]
2010/12/10 下午 07:28:00
查了一下網路相關的意見。
這樣呼叫解構子,應是標準C++允許的:
pMyObj->~CMyClass();//<--"標準"模板程式館(STL)都用了,還不標準?^^

既然可以呼叫解構子,當然也該可以呼叫建構子囉?
答案是肯定的,就是使用 placement new!
為什麼不提供直接呼叫建構子,而用 placement new?
因為要程式員明示呼叫建構子確實是為了創建物件的過程。
而不是英英美代子就呼叫建構子一下(有後遺症)
有些編譯器(包含我用的VC6)則允許通過下面的語法來呼叫建構子:
pMyObj->CMyClass::CMyClass(...)
表示微軟希望你英英美代子可以胡搞,這樣你的程式才可以和微軟的並駕其驅。
(一樣的漏洞百出^^)
作者 : sflam(Raymond)討論區板主 Visual C++ .NET卓越專家VC++一代宗師新手入門優秀好手資訊類作業求救頂尖高手C++一代宗師貼文超過4000則
[ 貼文 4945 | 人氣 9172 | 評價 32290 | 評價/貼文 6.53 | 送出評價 142 次 ] 
[ 給個讚 ]  [ 給個讚 ]  [ 回應本文 ]  [ 發表新文 ]  [ 回上頁 ] [ 回討論區列表 ] [ 回知識入口 ]
2010/12/10 下午 09:28:52
>>>呼叫
>>> MyClass::MyClass();
>>>的意思是 以 default constructor 建構無名物件後馬上解構.
>>
>>那用:
>>  MyClass();
>>
>>就好了.
>
>多加一個叫 MyClass() 的一般函數,就會發現用 MyClass(); 編譯器沒法判斷了,

如果我沒記錯的話, 同名的函式會把同名的 class 隱藏起來. 讓我找找看...找到了
  3.3.10 Name hiding

  2. A class name or enumeration name can be hidden by the name of a variable, data memeber, function, or enumeration declared in the same scope. if a class or enumeration name and a variable, data member, function, or enumerator are declared in the same scope (in any order) with the same name, the class or enumeration name is hidden wherever the variable, data member, function, or enumerator name is visible.

>MyClass::MyClass(); 有其必要性

Too bad. 12.1p1 及 p2 都否定了這個可能性. 任何嚴格遵守標準的編譯器都不會編譯這一句.
作者 : sflam(Raymond)討論區板主 Visual C++ .NET卓越專家VC++一代宗師新手入門優秀好手資訊類作業求救頂尖高手C++一代宗師貼文超過4000則
[ 貼文 4945 | 人氣 9172 | 評價 32290 | 評價/貼文 6.53 | 送出評價 142 次 ] 
[ 給個讚 ]  [ 給個讚 ]  [ 回應本文 ]  [ 發表新文 ]  [ 回上頁 ] [ 回討論區列表 ] [ 回知識入口 ]
2010/12/10 下午 10:15:27
>>如果可以的話, 那這個應該也是可以的:
>>  Myclass obj;
>>  obj.MyClass();
>>但它是編譯錯誤.
>>有些編譯器允許:
>>  obj.MyClass::MyClass();
>
>呼叫MyClass()會和和建構MyClass物件的MyClass()產生 ambigous。
>所以要呼叫建構子要加上 MyClass:: 明白指定後面跟著的是MyClass的成員。
>若要呼叫解構子,因為沒有ambigous的問題,所以可以這樣呼叫:
>obj.~MyClass();

這個不是 ambiguous 的問題. MyClass::MyClass(); 是錯誤的原因是因為建構子沒有名字, 不能被 lookup.

VC++ 一直都有

>>但跟 『MyClass::MyClass();』 一樣, 都是非標準的行為.
>我不知道'手動'呼叫建構和解構是不是標準,但在一些template container

解構子可以明確的呼叫. 這是允許的 12.4
  10. ... Destructors can also be invoked explicitly.

  12. In an explicit destructor call, the destructor name appears as a ~ followed by a type-name that names the destructor's class type. ...

唯一可以「呼叫」建構子的地方是 placement new. 如果沒記錯的話, 正式名字好像叫做 "in-place construction".

一個比較簡單的例子:
    #include <new>

    class MyClass
    {
    };

    int main()
    {
     char memory[sizeof(MyClass)];
     Myclass *p = new (memory)MyClass();
     /* ... */
     p->~MyClass();
    }
作者 : cxxlman(CxxlMan) C++優秀好手貼文超過1000則
[ 貼文 1042 | 人氣 3227 | 評價 1260 | 評價/貼文 1.21 | 送出評價 28 次 ] 
[ 給個讚 ]  [ 給個讚 ]  [ 回應本文 ]  [ 發表新文 ]  [ 回上頁 ] [ 回討論區列表 ] [ 回知識入口 ]
2010/12/11 上午 12:48:31
/*
** 請用 DEV C++ 編譯
** 我把先前討論以我個人見解匯整如下程式
** 免得太過零亂變成各說各話
** 我有用 vs2008 試著編譯,但其對 A::A 並沒有全面支援
** A::A 表示 A 命名空間的 A 形別的意思
*/


#include <cstdlib>
#include <iostream>

using namespace std;

class A
{
  char *ch;
public:
  A()
  {
    cout << "A 被建構,在此之前 A 物件的記憶體空間已被配置好了" << endl;
    ch = new char; // 第二次執行,會讓第一次配置的記憶體成為沒法回收的垃圾
  }
  ~A()
  {
    delete ch;
    cout << "A 被解構,但目前 A 物件的記憶體空間仍存在" << endl;
  }
};


void A()
{
  cout << "我只是一般函數" << endl;
}

int main(int argc, char *argv[])
{

  A::A (); // 建構無名物件, 刻意把小括號弄遠點,免得被看成函數呼叫

  A::A Name_Obj(); // 建構有名物件,是不是和上一個很像

  A(); // 呼叫一般函數

  A::A *a_obj = new A::A; // 後面沒加括號
  // a_obj->A::A(); // VS2005 才能這樣做,可能是認為 a_obj->A::~A() 後可再用此方法再建構一次吧

  char buf[100];
  
  A::A *a_obj2 = new((void *)buf) A::A(); // 這堥頝N加小括號,A::A 是代表形別,不會被看成函數吧
  a_obj2->A::~A(); // 因是由 new((void *)buf) 配置出來的,不能使用 delete 指令,所以要直接呼叫解構函數
  

    system("PAUSE");
    return EXIT_SUCCESS;
}
作者 : sunyear(coco) VC++卓越專家C++頂尖高手貼文超過2000則
[ 貼文 2421 | 人氣 1485 | 評價 6060 | 評價/貼文 2.5 | 送出評價 5 次 ] 
[ 給個讚 ]  [ 給個讚 ]  [ 回應本文 ]  [ 發表新文 ]  [ 回上頁 ] [ 回討論區列表 ] [ 回知識入口 ]
2010/12/11 上午 01:02:34
當意見漸趨一致時,就是真相大白之時^^

>>MyClass::MyClass(); 有其必要性
換個說法:有些時候MyClass::MyClass();不是脫褲子放屁 ^^

>>呼叫MyClass()會和和建構MyClass物件的MyClass()產生 ambigous。
>這個不是 ambiguous 的問題. MyClass::MyClass(); 是錯誤的原因是
>因為建構子沒有名字, 不能被 lookup.
同意這不能說是 ambigous。既然設計了 placement new 的呼叫方式,
那麼應該就是不允許使用成員函式的方式呼叫建構子才是合理。
一個不能確定的問題是:VC6 允許MyObj.CMyClass::CMyClass()
呼叫建構子,是故意還是一個失誤(BUG)?
如果是故意的,那也沒什麼好談,微軟就是老大,你能怎樣?
如果新版VC不允許MyObj.CMyClass::CMyClass(),那麼可判斷它是VC6的一個失誤。
(可惜我原安裝新版的VC刪了不能試一下,太多要維護的程式都需要VC6)
如果是失誤,是怎麼造成的呢?
從MyObj.CMyClass(); VC6編譯時錯誤的資訊看來,它是把CMyClass當成type不是建構子。
據此推測編譯器的預想是CMyClass名若先被當成type看待,自然不會有建構子被看見的問題。
所以編譯器的paser並沒有設計刻意去隱藏它。很不幸的
MyObj.CMyClass::CMyClass()中的scope巧妙的避開了CMyClass()是個type的檢查。
所以建構子就這樣意外的被呼叫了。

>解構子可以明確的呼叫. 這是允許的 12.4
>  10. ... Destructors can also be invoked explicitly.
加上這個,應該是拍板定案了。

作者 : sflam(Raymond)討論區板主 Visual C++ .NET卓越專家VC++一代宗師新手入門優秀好手資訊類作業求救頂尖高手C++一代宗師貼文超過4000則
[ 貼文 4945 | 人氣 9172 | 評價 32290 | 評價/貼文 6.53 | 送出評價 142 次 ] 
[ 給個讚 ]  [ 給個讚 ]  [ 回應本文 ]  [ 發表新文 ]  [ 回上頁 ] [ 回討論區列表 ] [ 回知識入口 ]
2010/12/11 上午 02:20:54
>/*
>** 請用 DEV C++ 編譯

請不要用 Dev-C++ 編譯, Dev-C++ 已經很久沒有更新了, Dev-C++ 5.0 beta 版底下還是用 3.4.2 版的 gcc.

新版的 gcc 已經把 A::A(); 列為錯誤.
我用 4.5.0 版的 MinGW 來編譯, 結果如下:

test.cpp: In function 'int main(int, char**)':
test.cpp:31:9: error: cannot call constructor 'A::A' directly
test.cpp:31:9: error: for a function-style cast, remove the redundant '::A'
test.cpp:33:3: error: 'A::A' names the constructor, not the type
test.cpp:33:8: error: expected ';' before 'Name_Obj'
test.cpp:33:18: error: statement cannot resolve address of overloaded function
test.cpp:37:9: error: 'a_obj' was not declared in this scope
test.cpp:37:21: error: expected type-specifier
test.cpp:37:21: error: expected ';'
test.cpp:42:9: error: 'a_obj2' was not declared in this scope
test.cpp:42:35: error: expected type-specifier
test.cpp:42:35: error: expected ';'

作者 : cxxlman(CxxlMan) C++優秀好手貼文超過1000則
[ 貼文 1042 | 人氣 3227 | 評價 1260 | 評價/貼文 1.21 | 送出評價 28 次 ] 
[ 給個讚 ]  [ 給個讚 ]  [ 回應本文 ]  [ 發表新文 ]  [ 回上頁 ] [ 回討論區列表 ] [ 回知識入口 ]
2010/12/11 下午 02:20:08
好不容易才安裝好 MinGW, 也好不容易才設定好讓 Dev 勉強能用 MinGW 做編譯, 只是有錯誤時不能像以往一樣能用滑鼠一點就帶到出錯的地方

新版 gcc 把 A::A 拿掉可避免爭議, 但方便性也沒了, 至少在 vs2008 並不甩他仍部份保留這美麗的錯誤

A::A 現不能用, class A 又被一般函數 A() 擋掉, 要怎麼使用 class A 呢, 我本來想用如下

namespace A{
class A
{
  ...
};
}

但 namespace A 仍會被一般函數 A() 擋掉= =" 只好把整個程式改成如下

但仍覺得 A::A 的方式比較好用, 那些標準製定單位怎不考慮向下相容, A::A 就算不正當其實也無傷大雅, A:: 底下的確沒有 class A 這東西, 但把自己視為自己命名空間的一份子也不算過份吧


#include <cstdlib>
#include <iostream>

using namespace std;

namespace AA{
class A
{
  char *ch;
public:
  A()
  {
    cout << "A 被建構,在此之前 A 物件的記憶體空間已被配置好了" << endl;
    ch = new char; // 第二次執行,會讓第一次配置的記憶體成為沒法回收的垃圾
  }
  ~A()
  {
    delete ch;
    cout << "A 被解構,但目前 A 物件的記憶體空間仍存在" << endl;
  }
};
}

void A()
{
  cout << "我只是一般函數" << endl;
}

int main(int argc, char *argv[])
{

  AA::A (); // 建構無名物件, 刻意把小括號弄遠點,免得被看成函數呼叫

  AA::A Name_Obj(); // 建構有名物件,是不是和上一個很像

  A(); // 呼叫一般函數

  AA::A *a_obj = new AA::A; // 後面沒加括號
  // a_obj->AA::A::A(); // VS2005 才能這樣做,可能是認為 a_obj->AA::A::~A() 後可再用此方法再建構一次吧

  char buf[100];
  
  AA::A *a_obj2 = new((void *)buf) AA::A(); // 這堥頝N加小括號,AA::A 是代表形別,不會被看成函數吧
  a_obj2->AA::A::~A(); // 因是由 new((void *)buf) 配置出來的,不能使用 delete 指令,所以要直接呼叫解構函數
  

    system("PAUSE");
    return EXIT_SUCCESS;
}
作者 : sunyear(coco) VC++卓越專家C++頂尖高手貼文超過2000則
[ 貼文 2421 | 人氣 1485 | 評價 6060 | 評價/貼文 2.5 | 送出評價 5 次 ] 
[ 給個讚 ]  [ 給個讚 ]  [ 回應本文 ]  [ 發表新文 ]  [ 回上頁 ] [ 回討論區列表 ] [ 回知識入口 ]
2010/12/11 下午 09:48:28
>A::A 現不能用, class A 又被一般函數 A() 擋掉, 要怎麼使用 class A 呢
可以這樣寫:
class A();//<--A是class不是函式

作者 : cxxlman(CxxlMan) C++優秀好手貼文超過1000則
[ 貼文 1042 | 人氣 3227 | 評價 1260 | 評價/貼文 1.21 | 送出評價 28 次 ] 
[ 給個讚 ]  [ 給個讚 ]  [ 回應本文 ]  [ 發表新文 ]  [ 回上頁 ] [ 回討論區列表 ] [ 回知識入口 ]
2010/12/11 下午 11:18:40

>>A::A 現不能用, class A 又被一般函數 A() 擋掉, 要怎麼使用 class A 呢
>可以這樣寫:
>class A();//<--A是class不是函式

用 gcc 4.5.0 試過, 不行
作者 : sunyear(coco) VC++卓越專家C++頂尖高手貼文超過2000則
[ 貼文 2421 | 人氣 1485 | 評價 6060 | 評價/貼文 2.5 | 送出評價 5 次 ] 
[ 給個讚 ]  [ 給個讚 ]  [ 回應本文 ]  [ 發表新文 ]  [ 回上頁 ] [ 回討論區列表 ] [ 回知識入口 ]
2010/12/11 下午 11:43:45
>>class A();//<--A是class不是函式
>用 gcc 4.5.0 試過, 不行
不會吧!?
[ class ] tag declarators;//<--這應該是C++的標準語法
只是大家習慣把class省略,久了就忘了它的存在。
既然tag(class名稱)有衝突,就不能省略 class就個關鍵字。
那gcc可以編譯下面的宣告嗎?
class A aObj;
作者 : cxxlman(CxxlMan) C++優秀好手貼文超過1000則
[ 貼文 1042 | 人氣 3227 | 評價 1260 | 評價/貼文 1.21 | 送出評價 28 次 ] 
[ 給個讚 ]  [ 給個讚 ]  [ 回應本文 ]  [ 發表新文 ]  [ 回上頁 ] [ 回討論區列表 ] [ 回知識入口 ]
2010/12/11 下午 11:58:18
我本來也這麼想
class A(); 真的不行
class A A_Obj(); 是可以
作者 : sunyear(coco) VC++卓越專家C++頂尖高手貼文超過2000則
[ 貼文 2421 | 人氣 1485 | 評價 6060 | 評價/貼文 2.5 | 送出評價 5 次 ] 
[ 給個讚 ]  [ 給個讚 ]  [ 回應本文 ]  [ 發表新文 ]  [ 回上頁 ] [ 回討論區列表 ] [ 回知識入口 ]
2010/12/12 上午 12:21:11
>我本來也這麼想
>但 class A(); 真的不行
>class A A_Obj(); 是可以
我覺得那是gcc parser的失誤。
但只要gcc還認得 class A aObj;就還有解。
使用 typedef 定義 class A:
typedef class A A_class;
作者 : sunyear(coco) VC++卓越專家C++頂尖高手貼文超過2000則
[ 貼文 2421 | 人氣 1485 | 評價 6060 | 評價/貼文 2.5 | 送出評價 5 次 ] 
[ 給個讚 ]  [ 給個讚 ]  [ 回應本文 ]  [ 發表新文 ]  [ 回上頁 ] [ 回討論區列表 ] [ 回知識入口 ]
2010/12/12 上午 12:29:39
typedef class A A_class;

A_class();//<--代替class A();
作者 : cxxlman(CxxlMan) C++優秀好手貼文超過1000則
[ 貼文 1042 | 人氣 3227 | 評價 1260 | 評價/貼文 1.21 | 送出評價 28 次 ] 
[ 給個讚 ]  [ 給個讚 ]  [ 回應本文 ]  [ 發表新文 ]  [ 回上頁 ] [ 回討論區列表 ] [ 回知識入口 ]
2010/12/12 上午 12:42:37
賓果,可以

現在 dev c++ 配 gcc 4.5.0 太難用了
正在抓 Code::Blocks 來試試
作者 : cxxlman(CxxlMan) C++優秀好手貼文超過1000則
[ 貼文 1042 | 人氣 3227 | 評價 1260 | 評價/貼文 1.21 | 送出評價 28 次 ] 
[ 給個讚 ]  [ 給個讚 ]  [ 回應本文 ]  [ 發表新文 ]  [ 回上頁 ] [ 回討論區列表 ] [ 回知識入口 ]
2010/12/12 下午 02:27:18
剛安裝 Code::Blocks 還在摸索適應中
不過他內附的 gcc 4.4.1 版對 A::A 還是有支援的
可能是到 4.5.0 版才改為不行
也許是一時失誤 4.5.1 版又會改回來了也說不定 ^^
作者 : angleevil(邪月) 貼文超過200則人氣指數超過10000點
[ 貼文 485 | 人氣 18722 | 評價 720 | 評價/貼文 1.48 | 送出評價 39 次 ] 
[ 給個讚 ]  [ 給個讚 ]  [ 回應本文 ]  [ 發表新文 ]  [ 回上頁 ] [ 回討論區列表 ] [ 回知識入口 ]
2010/12/13 上午 10:25:35
其實有時候在宣告一個物件(object)時,就會初始化(預設建構子的作用)

但是之後的敘述中,也有將物件再做初始化的動作,
因此有人會在myClass()去給予初始值,例如下面例子
myclass myObject;
...略...

myObject = myClass();

至於直接用myClass::myClass()去動作,
既然討論到現在,確定是個美麗的錯誤,那就避免使用他吧
畢竟正確的觀念導致早點下班,也是好事

ps:
這篇有50篇之長,我看到中途就沒看了,
其實要解決版本的爭論,還有一個辦法,
把compiler的debug的等級提到最高,
也就是最嚴格的狀態,我猜myClass::myClass(),不會通過的





作者 : sunyear(coco) VC++卓越專家C++頂尖高手貼文超過2000則
[ 貼文 2421 | 人氣 1485 | 評價 6060 | 評價/貼文 2.5 | 送出評價 5 次 ] 
[ 給個讚 ]  [ 給個讚 ]  [ 回應本文 ]  [ 發表新文 ]  [ 回上頁 ] [ 回討論區列表 ] [ 回知識入口 ]
2010/12/13 上午 11:24:54
To:邪月
我幫你把最後在討論的議題縮小吧(雖然已經脫離了原樓主的議題)
>因此有人會在myClass()去給予初始值,例如下面例子
>myclass myObject;
>...略...
>myObject = myClass();
>至於直接用myClass::myClass()去動作,
>既然討論到現在,確定是個美麗的錯誤,那就避免使用他吧
>畢竟正確的觀念導致早點下班,也是好事

CxxlMan 的意思是
myObject = myClass();//<--myClass會和和同名的函式名稱同名而產生錯誤
myObject = myClass::myClass();//<--這個所謂美麗的錯誤,是因它可以解決和函式同名問題
我提出正規的解決和函式同名問題是應該要指名 class:
myObject = class myClass();//<--這樣myClass就不會被誤認是函式了
CxxlMan發現gcc不認識myObject = class myClass();
所以我只好提出讓gcc認識的myObject = class myClass();的方式:
typedef class myclass IamClass;
myObject = IamClass();//<--終結gcc同名問題的版本
作者 : cxxlman(CxxlMan) C++優秀好手貼文超過1000則
[ 貼文 1042 | 人氣 3227 | 評價 1260 | 評價/貼文 1.21 | 送出評價 28 次 ] 
[ 給個讚 ]  [ 給個讚 ]  [ 回應本文 ]  [ 發表新文 ]  [ 回上頁 ] [ 回討論區列表 ] [ 回知識入口 ]
2010/12/13 上午 11:35:43

>其實有時候在宣告一個物件(object)時,就會初始化(預設建構子的作用)
>
>但是之後的敘述中,也有將物件再做初始化的動作,
>因此有人會在myClass()去給予初始值,例如下面例子
>myclass myObject;
>...略...
>
>myObject = myClass();

這會牽扯到 assignment operator 和 copy constructor,又是另一個議題了

>至於直接用myClass::myClass()去動作,
>既然討論到現在,確定是個美麗的錯誤,那就避免使用他吧
>畢竟正確的觀念導致早點下班,也是好事

myClass:: 這個原本的用意只是為了辨識用的,可是後來 namespace 越搞越規化了,弄得像那種樹狀結構檔案目錄系統,使得 myClass:: 處境變得很尷尬,問題是這東西由來以久,以電腦界這種講究向下相容的特性,會這樣壯士斷腕還真難得,若全能這樣做也不用搞出 java、c# 了,C++ 一定一日千里
作者 : cxxlman(CxxlMan) C++優秀好手貼文超過1000則
[ 貼文 1042 | 人氣 3227 | 評價 1260 | 評價/貼文 1.21 | 送出評價 28 次 ] 
[ 給個讚 ]  [ 給個讚 ]  [ 回應本文 ]  [ 發表新文 ]  [ 回上頁 ] [ 回討論區列表 ] [ 回知識入口 ]
2011/5/13 下午 09:10:05
哈哈 不小心發現 A::A() 在 gcc 4.5.0 不能編譯的問題有了正解了
只要改成 typename A::A() 就行了
以前以為 typename 這個關鍵字只是 template 上的用的
現在才知 typename 還有其他的用意
以下把上面提出程式修改後列於下

#include <cstdlib>
#include <iostream>

using namespace std;

class A
{
  char *ch;
public:
  A()
  {
    cout << "A 被建構,在此之前 A 物件的記憶體空間已被配置好了" << endl;
    ch = new char; // 第二次執行,會讓第一次配置的記憶體成為沒法回收的垃圾
  }
  ~A()
  {
    delete ch;
    cout << "A 被解構,但目前 A 物件的記憶體空間仍存在" << endl;
  }
};


void A()
{
  cout << "我只是一般函數" << endl;
}

int main(int argc, char *argv[])
{

  typename A::A (); // 建構無名物件, 刻意把小括號弄遠點,免得被看成函數呼叫

  typename A::A Name_Obj(); // 建構有名物件,是不是和上一個很像

  A(); // 呼叫一般函數

  typename A::A *a_obj = new typename A::A; // 後面沒加括號
  // a_obj->A::A(); // VS2005 才能這樣做,可能是認為 a_obj->A::~A() 後可再用此方法再建構一次吧

  char buf[100];

  typename A::A *a_obj2 = new((void *)buf) typename A::A(); // 這堥頝N加小括號,A::A 是代表形別,不會被看成函數吧
  a_obj2->A::~A(); // 因是由 new((void *)buf) 配置出來的,不能使用 delete 指令,所以要直接呼叫解構函數


    system("PAUSE");
    return EXIT_SUCCESS;

}
作者 : sunyear(coco) VC++卓越專家C++頂尖高手貼文超過2000則
[ 貼文 2421 | 人氣 1485 | 評價 6060 | 評價/貼文 2.5 | 送出評價 5 次 ] 
[ 給個讚 ]  [ 給個讚 ]  [ 回應本文 ]  [ 發表新文 ]  [ 回上頁 ] [ 回討論區列表 ] [ 回知識入口 ]
2011/5/15 上午 01:06:45
>哈哈 不小心發現 A::A() 在 gcc 4.5.0 不能編譯的問題有了正解了
>只要改成 typename A::A() 就行了
>以前以為 typename 這個關鍵字只是 template 上的用的
看覺得"以前以為"的才是對的 ^^
typename A::A() 應該不是標準C++允許的。
標準應該用
class A::A();
或是
class A();
typename 的出現,是用來取代template中參數的class keyword。
(例如< class T>改用<typename T>)
為什麼要出現typename?
因為class是自定型別的keyword,但若template的參數是非自定型別呢?
例如T是int呢?在語言最大應用彈性的需求,當然是要允許。
從物件的觀點,int 作為物件的型別當然是沒問題;
但int絕對不是自定型別,套在class之下總是讓人覺得不倫不類。
最後才新增typename這個keyword來取代。
不論是自定型別(class)或非自定型別的名稱都是typename,看起來順眼多了。
但為了相容理由,class使用在template的功能還是維持不變。
作者 : cxxlman(CxxlMan) C++優秀好手貼文超過1000則
[ 貼文 1042 | 人氣 3227 | 評價 1260 | 評價/貼文 1.21 | 送出評價 28 次 ] 
[ 給個讚 ]  [ 給個讚 ]  [ 回應本文 ]  [ 發表新文 ]  [ 回上頁 ] [ 回討論區列表 ] [ 回知識入口 ]
2011/5/15 下午 12:24:52

typename 這個應是標準吧,請 R大 說明

以下這個例子就非得用 typename 不可

template <typename T, typename K>
class Tree
{
  class Node2
  {
    T m_t;
    K m_k;
  public:
    Node2(T t,K k)
    {
     m_t = t;
     m_k = k;
    }
    void Set(T t,K k)
    {
     m_t = t;
     m_k = k;
    }
  };

  Node2 *Func1(Node2 *n)
  {
    n->Set(1,2);
    return n;
  }

  Node2 *Func2(Node2 *n); // 和 Func1 功能一樣但提到外面做

public:
  Tree()
  {
  }
};

template <typename T,typename K>
typename Tree<T,K>::Node2 *Tree<T,K>::Func2(typename Tree<T,K>::Node2 *n)
{
  n->Set(1,2);
  return n;
}

看看 typename Tree<T,K>::Node2 這個怎麼看都像脫褲子放屁,可是不這樣寫又不行 @@
作者 : sunyear(coco) VC++卓越專家C++頂尖高手貼文超過2000則
[ 貼文 2421 | 人氣 1485 | 評價 6060 | 評價/貼文 2.5 | 送出評價 5 次 ] 
[ 給個讚 ]  [ 給個讚 ]  [ 回應本文 ]  [ 發表新文 ]  [ 回上頁 ] [ 回討論區列表 ] [ 回知識入口 ]
2011/5/15 下午 04:04:34
>template <typename T,typename K>
>typename Tree<T,K>::Node2 *Tree<T,K>::Func2(typename Tree<T,K>::Node2 *n)
>{
> n->Set(1,2);
> return n;
>}
>看看 typename Tree<T,K>::Node2 這個怎麼看都像脫褲子放屁,可是不這樣寫又不行 @@
typename 用在 template 是合法啊!
我說的是用在和template無關的地方,如你原先的例子:
typename A::A();//A不是template class
另你新舉的例子:
typename Tree<T,K>::Node2 *Tree<T,K>::Func2(typename Tree<T,K>::Node2 *n)
{
}

我覺得那個typename應是可以省略的,不能省略,應該又是gcc的問題:
Tree<T,K>::Node2 *Tree<T,K>::Func2(typename Tree<T,K>::Node2 *n) //<--這樣應是合法的
{
}
作者 : cxxlman(CxxlMan) C++優秀好手貼文超過1000則
[ 貼文 1042 | 人氣 3227 | 評價 1260 | 評價/貼文 1.21 | 送出評價 28 次 ] 
[ 給個讚 ]  [ 給個讚 ]  [ 回應本文 ]  [ 發表新文 ]  [ 回上頁 ] [ 回討論區列表 ] [ 回知識入口 ]
2011/5/15 下午 06:31:26

剛在 gcc 4.5.0 和 vs2008 試過了,

typename Tree<T,K>::Node2 一定要這樣寫不能省
作者 : sunyear(coco) VC++卓越專家C++頂尖高手貼文超過2000則
[ 貼文 2421 | 人氣 1485 | 評價 6060 | 評價/貼文 2.5 | 送出評價 5 次 ] 
[ 給個讚 ]  [ 給個讚 ]  [ 回應本文 ]  [ 發表新文 ]  [ 回上頁 ] [ 回討論區列表 ] [ 回知識入口 ]
2011/5/16 上午 12:01:25
>剛在 gcc 4.5.0 和 vs2008 試過了,
>typename Tree<T,K>::Node2 一定要這樣寫不能省
抱歉,已經習慣於VC6的環境了。
你這麼一提,我想起了以前看STL時,有類似的說明。
在型態名稱還無法確認時,要加上typename明白指出名稱是一個型態。
例如:
list<T>::iterator TheIterator;
template還沒俱現化時,還不能確認list<T>::iterator
是個type name還是list<T>::下的一個靜態成員。
為了能讓編譯器可以錯認,所以加上typename應是需要的。
VC早期版木可以不加,但應該會有靜態成員被誤認為型態名稱
的情況。查了一下VC在2003之後以經修正了這個問題。
typename Tree<T,K>::Node2 //加上這個 typename 是對的。

回到原先的問題,我知道的 typename 應該還是用在template裡面的。
template<class T>//這個class不是自訂型別class而已,struct也是,它也包含非自訂型別
所以用一個較廣義的typename取代class是有其正當性。

但C++還一直在演化,例如我覺得很重要的concept在最新的C++版本
中被取消了。原因如何,我不知道,但"大人"們應該有深入的考量吧!
typename"大人"會不會一樣有什麼特別的考量而跳出template之外?
我不知道,但總得不需要。
因為在template之外,內建型別,自訂型別是要清楚的分別的。
該int就是int,該struct或class就struct或class。
用typename取代,反而模糊了語義。
作者 : sunyear(coco) VC++卓越專家C++頂尖高手貼文超過2000則
[ 貼文 2421 | 人氣 1485 | 評價 6060 | 評價/貼文 2.5 | 送出評價 5 次 ] 
[ 給個讚 ]  [ 給個讚 ]  [ 回應本文 ]  [ 發表新文 ]  [ 回上頁 ] [ 回討論區列表 ] [ 回知識入口 ]
2011/5/16 下午 06:30:33
要下班了,查一下有關 C++ Type Specifiers(當休閒一下^^)
MSN VS 2010 這樣說--
C++ Type Specifiers:
simple-type-name
 class-specifier
 enum-specifier
 elaborated-type-specifier
 :: class-name
 const
 volatile
;-----------------------
elaborated-type-specifier:
 class identifier
 struct identifier
 union identifier
 enum identifier
============================================
Standard for Programming Language C++ N2914這樣說--
type-specifier:
 simple-type-specifier
 class-specifier
 enum-specifier
 elaborated-type-specifier
 typename-specifier //注意到這個
 cv-qualifier
============================================
兩個文件都是在一般的型態的宣告章節,不是temlate的章節
從 VS2010 看起來,VS是堅持 typename 不能在template之外的地方出現的。
但N2914是乎有意無意偷偷的放了進來。
(用偷偷一詞是從此以往,除了template章節外,typename 一詞未再出現)
這表示C++的標準允許typename使用在template之外的地方?

typename 本來就是為了解救class在template中,需要從自訂型別變裝成非自訂型別的尷尬。
進而讓它跳出template有意義嗎?個人很存疑,理由如下:(以下談的非指template)
程式員若要的是編譯器能自動識別型態名稱,那麼就省略type-specifier即可。
大部份的程式都這麼做,多年來也沒什麼問題。
程式員若要的編譯器可以檢知型態的錯用,那麼就規規矩矩的打class,struct,enum...
使用typename替代,目的好像是要編譯器能自動識別型態名稱,但有點脫褲子放屁。
作者 : cxxlman(CxxlMan) C++優秀好手貼文超過1000則
[ 貼文 1042 | 人氣 3227 | 評價 1260 | 評價/貼文 1.21 | 送出評價 28 次 ] 
[ 給個讚 ]  [ 給個讚 ]  [ 回應本文 ]  [ 發表新文 ]  [ 回上頁 ] [ 回討論區列表 ] [ 回知識入口 ]
2011/5/16 下午 09:29:44
用以下例子補充你的說明
編譯器 gcc 4.5.2


class A
{
public:
  A(){}
  ~A(){}

  class B
  {
  public:
    B(){}
    ~B(){}
  };
};

int main()
{

  A a1();
  class A a2();
// typename A a3(); // 不行

// A::A a4(); // 不行
  class A::A a5();
  typename A::A a6();


  A::B b1;
  class A::B b2;
  typename A::B b3;

  A();
// class A(); // 不行
// typename A(); // 不行

// A::A(); // 不行
// class A::A(); // 不行
  typename A::A();

  A::B();
// class A::B(); // 不行
  typename A::B();

    return 0;
}
作者 : sflam(Raymond)討論區板主 Visual C++ .NET卓越專家VC++一代宗師新手入門優秀好手資訊類作業求救頂尖高手C++一代宗師貼文超過4000則
[ 貼文 4945 | 人氣 9172 | 評價 32290 | 評價/貼文 6.53 | 送出評價 142 次 ] 
[ 給個讚 ]  [ 給個讚 ]  [ 回應本文 ]  [ 發表新文 ]  [ 回上頁 ] [ 回討論區列表 ] [ 回知識入口 ]
2011/5/16 下午 10:33:01
>看看 typename Tree<T,K>::Node2 這個怎麼看都像脫褲子放屁,可是不這樣寫又不行 @@

C++98 14.6 Name resolution
3. A qualified-name that refers to a type and that depends on a template-parameter shall be prefixed by the keyword typename to indicate that the qualified-name denotes a type, forming an elaborated-type specifier.

所以上面的 Tree<T,K>::Node2 必須前置關鍵字 「typename」, 讓編譯器知道 Tree<T,K>::Node2 是個類型, 而 Tree<T,K>::Func2() 是個回傳該類型指標的函式, 同時參數 'n' 是個指標.

這有其必要, 因為 specialization 的關係, template 內的 dependent name 無法分類. 這個句子 『Tree<T,K>::Node2 *n』可以是個宣告, 也可以是個 multiplication 的運算式.

在原來的 template 裡, 'Node2' 是個類型, 但在另一個 specialized 的 template 裡, 'Node2' 不一定是類型:

  template<> class Tree<void, void>
  {
  pulic:
    int Node2;
    ...
  };

  template<> class Tree<void, int>
  {
  public:
    enum { Node2 };
  };

作者 : cxxlman(CxxlMan) C++優秀好手貼文超過1000則
[ 貼文 1042 | 人氣 3227 | 評價 1260 | 評價/貼文 1.21 | 送出評價 28 次 ] 
[ 給個讚 ]  [ 給個讚 ]  [ 回應本文 ]  [ 發表新文 ]  [ 回上頁 ] [ 回討論區列表 ] [ 回知識入口 ]
2011/5/17 上午 11:36:15
我個人認為 typename Tree<T,K>::Node2 一句若 Node2 不是一個 class 會怎麼樣,編譯器也不會把它當 class 吧,Node2 是不是 class 編譯器都比程式設計師還清楚,typename 加不加上去會有什麼差別?

gcc 甚至還大幅擴大 typename 的權限,討論中的 A::A 也得用 typename 來指示類型,這也是標準嗎


  template<typename T,typename K> class Tree<int,int>
  {
  public:
    T Node2;
   ...
  };


作者 : sflam(Raymond)討論區板主 Visual C++ .NET卓越專家VC++一代宗師新手入門優秀好手資訊類作業求救頂尖高手C++一代宗師貼文超過4000則
[ 貼文 4945 | 人氣 9172 | 評價 32290 | 評價/貼文 6.53 | 送出評價 142 次 ] 
[ 給個讚 ]  [ 給個讚 ]  [ 回應本文 ]  [ 發表新文 ]  [ 回上頁 ] [ 回討論區列表 ] [ 回知識入口 ]
2011/5/18 上午 12:56:02
>我個人認為 typename Tree<T,K>::Node2 一句若 Node2 不是一個 class 會怎麼樣,編譯器也不會把它當 class 吧,Node2 是不是 class 編譯器都比程式設計師還清楚,

在這一點, 編譯器未必會比程式設計師清楚, 因為在沒有 instantiate 的情況下, template 不會產生 code, 也不會被編譯; 在沒有 template argument 資料的情況下, 編譯器只能做最基本的 syntax check.

>typename 加不加上去會有什麼差別?

有差別.

如果沒有 typname, 句子「Tree<T,K>::Node2 * x」可以是:
  - 定義一個 pointer 物件 x.
  - 一個相乘的運算式.

而語言標準會採用後者 (C++98 14.6 Name resolution, p2):

A name used in a template declaration or definition and that is dependent on a template-parameter is assumed not to name a type unless the applicable name lookup finds a type name or the name is qualified by the keyword typename.

typename 在這裡是用來確定句子的語義的, 這樣在沒有 instantiate 的情況下, 編譯器也能夠做出簡單的 parsing.

當然, 用 typename 並不表示 Tree<T,K>::Node2 就會自動成為類型. 在 instantiate 的時候, 編譯器會根據實際情況來編譯.


>gcc 甚至還大幅擴大 typename 的權限,討論中的 A::A 也得用 typename 來指示類型,這也是標準嗎

這不是標準的用法. 用 -std=c++98 來試試看.

作者 : cxxlman(CxxlMan) C++優秀好手貼文超過1000則
[ 貼文 1042 | 人氣 3227 | 評價 1260 | 評價/貼文 1.21 | 送出評價 28 次 ] 
[ 給個讚 ]  [ 給個讚 ]  [ 回應本文 ]  [ 發表新文 ]  [ 回上頁 ] [ 回討論區列表 ] [ 回知識入口 ]
2011/5/18 下午 08:46:59

>在這一點, 編譯器未必會比程式設計師清楚, 因為在沒有 instantiate 的情況下, template 不會產生 code, 也不會被編譯; 在沒有 template argument 資料的情況下, 編譯器只能做最基本的 syntax check.

須要做這種特別聲明大概有三種情況:1. 模擬兩可時 2. 強制轉形 3. 引用到尚未明白的形別時

這裡的 typename 用法應屬第 3 種,我是認為編譯器不可能不清楚,以下我把程式做到可正確執行的階段,發現編譯器不是不認識 Node2,而是不認識 Tree<T,K> ,但怎可能會不認識呢?為什麼會認識 Tree<T,K>::Func2 卻不知道 Tree<T,K>::Node2


#include <iostream>

using namespace std;

template <typename T, typename K>
class Tree
{
public:

 class Node2
 {
 public:

  T m_t;
  K m_k;

  Node2(T t,K k)
  {
   m_t = t;
   m_k = k;
  }
  void Set(T t,K k)
  {
   m_t = t;
   m_k = k;
  }
 }node;


 Node2 *Func2(Node2 *n);
 void Func3(Node2 *n);

 Tree(T t,K k)
  :node(t,k)
 {
 }
};

template <typename T,typename K>
typename Tree<T,K>::Node2 *Tree<T,K>::Func2(Node2 *n)
{
 n->Set(node.m_t,node.m_k);
 return n;
}

template <typename T,typename K>
void Tree<T,K>::Func3(Node2 *n)
{
 n->Set(node.m_t,node.m_k);
}


int main()
{
 Tree<int,int> Tree_Obj1(1,2);
 Tree<int,int> Tree_Obj2(100,200);

 Tree_Obj2.Func3(&Tree_Obj1.node);

 cout << "m_t: " << Tree_Obj1.node.m_t << endl;


 return 0;
}


>
>>typename 加不加上去會有什麼差別?
>
>有差別.
>
>如果沒有 typname, 句子「Tree<T,K>::Node2 * x」可以是:
>  - 定義一個 pointer 物件 x.
>  - 一個相乘的運算式.
>
>而語言標準會採用後者 (C++98 14.6 Name resolution, p2):
>
>A name used in a template declaration or definition and that is dependent on a template-parameter is assumed not to name a type unless the applicable name lookup finds a type name or the name is qualified by the keyword typename.
>
>typename 在這裡是用來確定句子的語義的, 這樣在沒有 instantiate 的情況下, 編譯器也能夠做出簡單的 parsing.
>
>當然, 用 typename 並不表示 Tree<T,K>::Node2 就會自動成為類型. 在 instantiate 的時候, 編譯器會根據實際情況來編譯.

class Node2 已宣告是 class ,應已足夠在沒有 instantiate 的情況下知道怎麼做了,不過這裡的問題在 Tree<T,K>,所以就不用討論這了

>
>>gcc 甚至還大幅擴大 typename 的權限,討論中的 A::A 也得用 typename 來指示類型,這也是標準嗎
>
>這不是標準的用法. 用 -std=c++98 來試試看.

不懂這標準是指 1998 定的意思嗎,還是會不斷進化修改
作者 : sflam(Raymond)討論區板主 Visual C++ .NET卓越專家VC++一代宗師新手入門優秀好手資訊類作業求救頂尖高手C++一代宗師貼文超過4000則
[ 貼文 4945 | 人氣 9172 | 評價 32290 | 評價/貼文 6.53 | 送出評價 142 次 ] 
[ 給個讚 ]  [ 給個讚 ]  [ 回應本文 ]  [ 發表新文 ]  [ 回上頁 ] [ 回討論區列表 ] [ 回知識入口 ]
2011/5/18 下午 09:43:16
>>這不是標準的用法. 用 -std=c++98 來試試看.
>
>不懂這標準是指 1998 定的意思嗎,還是會不斷進化修改

語言標準會隨著需要不斷的演化修改, 只是不可能像軟件那樣頻繁. 目前的 C++ 標準是在 1998 年制定的, 新的標準尚未定案. 為了區分這兩者, 一般把目前的標準用 C++98 來表示; 新的, 尚未定案的標準用 C++0x 來表示.

GCC 的 C 及 C++ 編譯器有自己的 language extensions, 可以用 command line option 『-std=』 來控制. 每個 option 都會有預設, 編譯 C++ 語言的預設是 『-std=gnu++98』, 它等於 『-std=c++98』 + extension.

C 語言也是一樣, gcc 的預設是 『-std=gnu90』, 它等於 『-std=c90』 + extension.

如果你用預設的來編譯, 成功編譯並不表示你的程式符合語言標準, 因為程式可能用到了 extension.

據我了解, GCC 並不 100% 符合語言標準, 而符合標準的語句也可能會有編譯錯誤 (編譯器的 bug), 但在編譯時把 extension 剔除掉可以減少很多誤會.

另一方面, Comeau C/C++ 是目前公認最為符合標準的編譯器, 它有一個 online 程式測試, 它不會產生 production code, 但可以利用它來檢測程式的標準性.

作者 : sunyear(coco) VC++卓越專家C++頂尖高手貼文超過2000則
[ 貼文 2421 | 人氣 1485 | 評價 6060 | 評價/貼文 2.5 | 送出評價 5 次 ] 
[ 給個讚 ]  [ 給個讚 ]  [ 回應本文 ]  [ 發表新文 ]  [ 回上頁 ] [ 回討論區列表 ] [ 回知識入口 ]
2011/5/19 上午 11:00:51
re:cxxlman
>class Node2 已宣告是 class ,應已足夠在沒有 instantiate 的情況下知道怎麼做了,
>不過這裡的問題在 Tree<T,K>,所以就不用討論這了
呵..我都認錯了,你還沒放下?^^
typename Tree<T,K>::Node2 *Tree<T,K>::Func2(Node2 *n)//<--這個typename是需要的
template 的解析和macro是不一樣的。舉個例子:
#define MyData 5
int GetMyData(){return MyData;}//OK!
但,若是把MyData定義到GetMyData的後頭,就不行了
int GetMyData(){return MyData;}//unknow MyData
#define MyData 5

這是因為Macro的解析是即時取代,再套用Macro之前Macro的定義必需先出現。
再看看一個class的例子:
class A{
public:
int Get(){return m_i};
private:
int m_i;
};
m_i出現在使用它之後,這是可以的。
因為class的解析是分幾個pass進行。
第一個pass分析Get()時會只產生一個框架,m_i當做一個變數(框),還未有實質東東。
(可以是成員或global,或跟本未定義)
當整個class 在第一個pass解析完後,進行第二個pass時才會對m_i這個變數(框)填入實質東西。
這樣的析順序,並不會因為m_i在Get之前或之後定義而有所不同。

template的解析就更複雜,但只要先想像class的解析就好。
第一個pass:
typename Tree<T,K>::Node2 *Tree<T,K>::Func2(Node2 *n)
編譯器就必需知道Tree<T,K>::Node2要放在型態框還是變數框,還是其它...
看前面class的解析的pass可以知道,不論 Node2的定義出現否。
這裡的Tree<T,K>::Node2 *Tree<T,K>::Func2(Node2 *n)都不會去參考它。
編譯器這裡只是要產生一個過渡的框架,但要知道是型態框還是變數框還是其它...
舉一個極端的例子:
Tree<T,K>::Node2 *WhoAmI;
這是宣告一個WhoAmI變數,型態是Tree<T,K>::Node2 *
還是WhoAmI乘Tree<T,K>::Node2這個變數
是差很大的過渡框。
所以必需明確的告訴編譯器Tree<T,K>::Node2到底是什麼:
typename Tree<T,K>::Node2 *Tree<T,K>::Func2(Node2 *n)
C++在template ,對於巢狀類別的型態成員的使用,需加上typename。
是因此而來。但有一個例外:
巢狀基類別的型態成員,可以不加,因為基類別會先於繼類別先解析完成。
作者 : sunyear(coco) VC++卓越專家C++頂尖高手貼文超過2000則
[ 貼文 2421 | 人氣 1485 | 評價 6060 | 評價/貼文 2.5 | 送出評價 5 次 ] 
[ 給個讚 ]  [ 給個讚 ]  [ 回應本文 ]  [ 發表新文 ]  [ 回上頁 ] [ 回討論區列表 ] [ 回知識入口 ]
2011/5/19 下午 05:45:22
下班回頭看了一下貼文,發現一個錯字:Pass 應該是 Parse。
順便俌充說明,所謂過渡框的意思:
這是我自創的名詞,因為不知道有什麼更好的名詞。
過渡框的意思是:編器產生了一個邏輯敘述,但敘述有一些空格待填。
所以邏輯敘述尚未完成,你可想像它是一個填空句,不是完成句。
例如前面的例子:Tree<T,K>::Node2 *WhoAmI;
因為編譯器並不確認Tree<T,K>::Node2是什麼東東,預設會把它當變數,
所以產生的填空句會是:
___ 乘以 WhoAmI
如果前面有加了typename:
typename Tree<T,K>::Node2 *WhoAmI;
那麼埴空句就變成:
宣告 WhoAmI ___ 型態的指標
空白的地方可以再下一個parse填,但填空句(框架)需完成。

當然你可以指出,聰明一點的編譯器是可以先parse可以完成的句字,
不能完成的句子先跳過,那麼先前parse過的句字就可以成為填空的元素。
而使得本來不能完成的句字變成可以完成。
這在class,strct等分析是可行的,但template太複雜使得要這樣做,技術上
有其很大的困難度。不論你怎麼完善它總會有疏漏的地方。
早期版本的VC應該就是這樣做(所以Tree<T,K>::Node2 *WhoAmI;是可以編譯成功)。
但,後來的VC也改成需要加typename了,可見微軟最後也不得不屈服。(我猜的)
作者 : cxxlman(CxxlMan) C++優秀好手貼文超過1000則
[ 貼文 1042 | 人氣 3227 | 評價 1260 | 評價/貼文 1.21 | 送出評價 28 次 ] 
[ 給個讚 ]  [ 給個讚 ]  [ 回應本文 ]  [ 發表新文 ]  [ 回上頁 ] [ 回討論區列表 ] [ 回知識入口 ]
2011/5/19 下午 06:01:33
我先把各種測試狀況列一下

typename Tree<T,K>::Node2 *Tree<T,K>::Func2(Node2 *n) // 可以

typename Tree<T,K>::Node2 *Tree<T,K>::Func2(Tree<T,K>::Node2 *n) // gcc 可以,vs2008 不行

typename Tree<T,K>::Node2 *Tree<T,K>::Func2(typename Tree<T,K>::Node2 *n) // 可以

Node2 *Tree<T,K>::Func2(Node2 *n) // 不行

Tree<T,K>::Node2 *Tree<T,K>::Func2(Node2 *n) // 不行

根據以上測試 typename 像是為了修飾 Tree<T,K> 而不是 Node2,不過我的看法仍是為了修飾 Node2,只是 Tree<T,K> 的不確定而沒法知道 Node2 是什麼,我的問題是編譯器是真不知道還是假裝不知道,為什麼可以知道 Tree<T,K>::Func2() 是函數,卻不知道 Tree<T,K>::Node2 是 class

我的看法是以目前的技術不用 typename 也能正確做出判斷,typename 在這的使用標準是比較過時了
作者 : sunyear(coco) VC++卓越專家C++頂尖高手貼文超過2000則
[ 貼文 2421 | 人氣 1485 | 評價 6060 | 評價/貼文 2.5 | 送出評價 5 次 ] 
[ 給個讚 ]  [ 給個讚 ]  [ 回應本文 ]  [ 發表新文 ]  [ 回上頁 ] [ 回討論區列表 ] [ 回知識入口 ]
2011/5/20 下午 12:01:50
>typename Tree<T,K>::Node2 *Tree<T,K>::Func2(Node2 *n) // 可以
引數的Node2已在Tree<T,K>::Func2()的scope,所以預設就是Tree<T,K>scope。

>typename Tree<T,K>::Node2 *Tree<T,K>::Func2(Tree<T,K>::Node2 *n) // gcc 可以,vs2008 不行
指定了scope,就表明放棄預設,一切照指定的scope進行解析。
有的編譯器可以省略引數的typename,是因前面已經表明Tree<T,K>::Node2是個typename了。
之後的解析就延用。但這不是標準規則。
標準規則是:使用巢狀相依(指template引數引起相依)的型態,就要加typename。
(翻的不是很順口,英文記得是 nested dependent type)

>typename Tree<T,K>::Node2 *Tree<T,K>::Func2(typename Tree<T,K>::Node2 *n) // 可以
標準寫法,當然可以。

>Node2 *Tree<T,K>::Func2(Node2 *n) // 不行
返回型態,不在Tree<T,K>::Func2 scope 範圍,應指明scope。
(但若寫在class Tree{};內,則可以)

>Tree<T,K>::Node2 *Tree<T,K>::Func2(Node2 *n) // 不行
Tree<T,K>::Node2 未加 typename

>根據以上測試 typename 像是為了修飾 Tree<T,K> 而不是 Node2...
typename是修飾Tree<T,K>::Node2,不是Tree<T,K>也不是Node2

>我的問題是編譯器是真不知道還是假裝不知道,為什麼可以知道 Tree<T,K>::Func2()
> 是函數,卻不知道 Tree<T,K>::Node2 是 class
Func2是Tree<T,K>的函式成員,Node2卻是Tree<T,K> class 下的 class(巢狀class)。
只要想想"巢狀"兩個字可以套在Node2上,卻不合適套在Func2上,就能分辨它們的不同。
你的例子是比較特殊的是Node2是Tree本身內的class,如前面我貼文所說的。要知道Node2
是個class,技術上是可行的。但若Node2不是Tree下的class而是另一個template class下的
class,就沒辦法了。
舉一個可以分辦dependent nest type 特例是簡單的,但要提出一個簡單規則:什麼時候可
以省略typename,什麼時候不可以省略,是困難的。規則複雜化的副作用就是程式員個自為政。
反正也搞不太清楚,只要使用的編譯器可以接受就行。結果就是程式可攜化降底了。今天在這個
編譯器可以,明天換一個編譯器不可以。就失去標準化的意義了。
所以簡單一點的規則:除基類相依巢狀型態外,相依巢狀型態就要加上typename。

我的看法是以目前的技術不用 typename 也能正確做出判斷,typename 在這的使用標準是比較過時了
像這樣的敘述:
typename Tree<T,K>::Node2 *Tree<T,K>::Func2(Tree<T,K>::Node2 *n)
即使在你使用的編譯器可以成功編譯,你可以說它比較聰明,但程式還是規矩的寫成:
typename Tree<T,K>::Node2 *Tree<T,K>::Func2(typename Tree<T,K>::Node2 *n)
免得在另一個編譯器下又過不了關。
作者 : sunyear(coco) VC++卓越專家C++頂尖高手貼文超過2000則
[ 貼文 2421 | 人氣 1485 | 評價 6060 | 評價/貼文 2.5 | 送出評價 5 次 ] 
[ 給個讚 ]  [ 給個讚 ]  [ 回應本文 ]  [ 發表新文 ]  [ 回上頁 ] [ 回討論區列表 ] [ 回知識入口 ]
2011/5/20 下午 12:08:30
訂正
>我的看法是以目前的技術不用 typename 也能正確做出判斷,
>typename 在這的使用標準是比較過時了
typename 是在template發明之後才新加上去的(原本只用class)。
除了解決語意上混淆外,也是有denpendent nested type 的問題要克服。
所以應該不會過時。

像這樣的敘述:
typename Tree<T,K>::Node2 *Tree<T,K>::Func2(Tree<T,K>::Node2 *n)
即使在你使用的編譯器可以成功編譯,你可以說它比較聰明,但程式還是規矩的寫成:
typename Tree<T,K>::Node2 *Tree<T,K>::Func2(typename Tree<T,K>::Node2 *n)
免得在另一個編譯器下又過不了關。
作者 : sunyear(coco) VC++卓越專家C++頂尖高手貼文超過2000則
[ 貼文 2421 | 人氣 1485 | 評價 6060 | 評價/貼文 2.5 | 送出評價 5 次 ] 
[ 給個讚 ]  [ 給個讚 ]  [ 回應本文 ]  [ 發表新文 ]  [ 回上頁 ] [ 回討論區列表 ] [ 回知識入口 ]
2011/5/20 下午 12:48:11
不然就略Scope,讓編譯器使用預設的scope rule:
typename Tree<T,K>::Node2 *Tree<T,K>::Func2(Node2 *n)
作者 : cxxlman(CxxlMan) C++優秀好手貼文超過1000則
[ 貼文 1042 | 人氣 3227 | 評價 1260 | 評價/貼文 1.21 | 送出評價 28 次 ] 
[ 給個讚 ]  [ 給個讚 ]  [ 回應本文 ]  [ 發表新文 ]  [ 回上頁 ] [ 回討論區列表 ] [ 回知識入口 ]
2011/5/20 下午 05:59:59
這應是 Parse 的問題吧,或者說在 Tree<T,K> 還沒確定時段時不能決定 Node2 的 scope,所以真正的問題還是 Parse,另外 scope 是指某個東西應歸屬於誰或者在某個領域內這個東西是不是存在,至於這東西是函數、變數或者其他什麼的不是 scope 所關注的,也和是不是巢狀無關

還有 Parse 的問題其實是編譯器這個軟體上的問題,不是語言本身的問題,不過也免不了會為了克服某些難題,會配合語言做些克難式的做法,只是用這種東西有它麻煩的地方,比如很難由語言本身直覺地瞭解它的作用和使用時機,或是對某個編譯器是個難題的東西但對其他編譯器不見得也是,這樣會對語言可攜性造成麻煩
作者 : sunyear(coco) VC++卓越專家C++頂尖高手貼文超過2000則
[ 貼文 2421 | 人氣 1485 | 評價 6060 | 評價/貼文 2.5 | 送出評價 5 次 ] 
[ 給個讚 ]  [ 給個讚 ]  [ 回應本文 ]  [ 發表新文 ]  [ 回上頁 ] [ 回討論區列表 ] [ 回知識入口 ]
2011/5/20 下午 08:05:27
>這應是 Parse 的問題吧,或者說在 Tree<T,K> 還沒確定時段時不能決定 Node2 的 scope...
>...至於這東西是函數、變數或者其他什麼的不是 scope 所關注的,也和是不是巢狀無關
當然和parse有關,不然前面何必解釋parse那麼多。
像你提的例子,少了typename,在語法分析並不難克服,但解得了其一,解不了其二。
你前面測試的例子,不是就發現某些情況是可以省略typename這個關鍵字嗎?
這表示不同的parse方法可以解決某些該加未加的typename的敘述。
但要把typename全部拿掉,語法分析會變得困難甚至不可能。
所以不應依賴編譯器,而是規定要加就加上去才是王道。
(可攜性的考量)

>還有 Parse 的問題其實是編譯器這個軟體上的問題,不是語言本身的問題,
>不過也免不了會為了克服某些難題,會配合語言做些克難式的做法,
>只是用這種東西有它麻煩的地方,比如很難由語言本身直覺地瞭解它的作用和使用時機,
>或是對某個編譯器是個難題的東西但對其他編譯器不見得也是,這樣會對語言可攜性造成麻煩
相同的語法造成的分析困難度都是一樣的,不管是不是C++編譯器。
語言的設計本來就應該考慮語法分析的難度和可行性。


作者 : cxxlman(CxxlMan) C++優秀好手貼文超過1000則
[ 貼文 1042 | 人氣 3227 | 評價 1260 | 評價/貼文 1.21 | 送出評價 28 次 ] 
[ 給個讚 ]  [ 給個讚 ]  [ 回應本文 ]  [ 發表新文 ]  [ 回上頁 ] [ 回討論區列表 ] [ 回知識入口 ]
2011/5/20 下午 09:10:29

>相同的語法造成的分析困難度都是一樣的,不管是不是C++編譯器。
>語言的設計本來就應該考慮語法分析的難度和可行性。

這是沒錯啦,只是面對同樣的問題所採取的策略可以不一樣,比如對 template class 這種複雜的東西可先置處理過再編譯,或者 Tree<T,K>::Node2 作為回傳值時,在 Parse 1 的階段先假設是 Tree<T,K> 的 class,這些都可免去使用 typename
作者 : sunyear(coco) VC++卓越專家C++頂尖高手貼文超過2000則
[ 貼文 2421 | 人氣 1485 | 評價 6060 | 評價/貼文 2.5 | 送出評價 5 次 ] 
[ 給個讚 ]  [ 給個讚 ]  [ 回應本文 ]  [ 發表新文 ]  [ 回上頁 ] [ 回討論區列表 ] [ 回知識入口 ]
2011/5/21 上午 02:39:07
>這是沒錯啦,只是面對同樣的問題所採取的策略可以不一樣,
>比如對 template class 這種複雜的東西可先置處理過再編譯,
>或者 Tree<T,K>::Node2 作為回傳值時,在 Parse 1 的階段
>先假設是 Tree<T,K> 的 class,這些都可免去使用 typename
先假設 Tree<T,K>::Node2 是 type name,那麼產生的填空框架
會是函式的定義。若是結果Tree<T,K>::Node2是個物件,那麼敘述
會變成表示式的框架和假設產生函式定義框架完全不搭。
結果會是個錯誤的分析。
先做前置處理再做框架分析,可以解決你例子的問題。
但若Node2不是Tree的巢狀class而是另一個相依class所定義呢:
template<class T>
struct Tree {
   T::Node2* pNode2Obj;
::::::::::::::::::::::::::::::::::::::::::
};
光前置處理Tree就不夠了,但還是有解,那就是先解class T。
若是class T又有類似巢狀相依的問題,就再先深一層續解。
想像一些情況,你可以發現那是理論可行,技術上困難。
就算做得出來,編譯器可能也會變得慢很多。
舉一個例子:
struct SA {
   typedef int tA;
};

template<class T>
struct TX {
   typedef SA tTX;
};

template<class T>
struct Tree {
   typedef TX<T>::tTX tTree1;
   typedef tTree1::tA tTree2;
};
:::::::::::::::::::繼續深層相依的宣告下去:::::::::::::::::::::::::::::::
從這個例子,可以看出來,深層分析的難度,也可能會用到大量的stack空間。
作者 : cxxlman(CxxlMan) C++優秀好手貼文超過1000則
[ 貼文 1042 | 人氣 3227 | 評價 1260 | 評價/貼文 1.21 | 送出評價 28 次 ] 
[ 給個讚 ]  [ 給個讚 ]  [ 回應本文 ]  [ 發表新文 ]  [ 回上頁 ] [ 回討論區列表 ] [ 回知識入口 ]
2011/5/21 下午 03:29:55

>>這是沒錯啦,只是面對同樣的問題所採取的策略可以不一樣,
>>比如對 template class 這種複雜的東西可先置處理過再編譯,
>>或者 Tree<T,K>::Node2 作為回傳值時,在 Parse 1 的階段
>>先假設是 Tree<T,K> 的 class,這些都可免去使用 typename
>先假設 Tree<T,K>::Node2 是 type name,那麼產生的填空框架
>會是函式的定義。若是結果Tree<T,K>::Node2是個物件,那麼敘述
>會變成表示式的框架和假設產生函式定義框架完全不搭。
>結果會是個錯誤的分析。

先假設是 type name 那麼這個框架就是 type name,也就是一個物件,怎麼會是一個函數呢

>先做前置處理再做框架分析,可以解決你例子的問題。
>但若Node2不是Tree的巢狀class而是另一個相依class所定義呢:
>template<class T>
>struct Tree {
> T::Node2* pNode2Obj;
>::::::::::::::::::::::::::::::::::::::::::
>};

Tree<T,K>::Node2 和 T::Node2 比較起來,前者的語意上看起來都比後者明確,Tree<T,K> 在未 instantiate 前就不能就用語意分析來了解Node2 是不是 class 嗎?也是我懷疑編譯器為什麼要加上 typename 的原因,後者才是有必要來討論要不要使用 typename

看看這句

typename Tree<T,K>::Node2 *Tree<T,K>::Func2(Tree<T,K>::Node2 *n)

在 gcc 是有能力處理的,也就是說在函數的參數 Tree<T,K>::Node2 被認知是一個 class,顯然編譯器是知道的,但為什麼作為回傳值又不知道了呢,我想是編譯器認為 Tree<T,K> 在外面和裡面可能不是相同的,也訧是說可能是如下:

typename Tree<int,int>::Node2 *Tree<float,float>::Func2(Tree<float,float>::Node2 *n)

像這種先做一些前置處理應是能解決


>光前置處理Tree就不夠了,但還是有解,那就是先解class T。
>若是class T又有類似巢狀相依的問題,就再先深一層續解。
>想像一些情況,你可以發現那是理論可行,技術上困難。
>就算做得出來,編譯器可能也會變得慢很多。
>舉一個例子:
>struct SA {
> typedef int tA;
>};
>
>template<class T>
>struct TX {
> typedef SA tTX;
>};
>
>template<class T>
>struct Tree {
> typedef TX<T>::tTX tTree1;
> typedef tTree1::tA tTree2;
>};
>:::::::::::::::::::繼續深層相依的宣告下去:::::::::::::::::::::::::::::::
>從這個例子,可以看出來,深層分析的難度,也可能會用到大量的stack空間。

你搞的太複雜了,就用你這例子

template<class T>
struct Tree {
   T::Node2* pNode2Obj;
::::::::::::::::::::::::::::::::::::::::::
};

Node2 絕不會是被識為 Tree<T>::Node2,若 T 是一個 class, Node2 是 T 中的一個 class 就讓它過關,若 T 不是 class 或 Node2 不是 T 中的 class ,編譯器就丟出 error 吧,加不加 typename 都沒差吧
作者 : cxxlman(CxxlMan) C++優秀好手貼文超過1000則
[ 貼文 1042 | 人氣 3227 | 評價 1260 | 評價/貼文 1.21 | 送出評價 28 次 ] 
[ 給個讚 ]  [ 給個讚 ]  [ 回應本文 ]  [ 發表新文 ]  [ 回上頁 ] [ 回討論區列表 ] [ 回知識入口 ]
2011/5/21 下午 05:15:25

>
>typename Tree<T,K>::Node2 *Tree<T,K>::Func2(Tree<T,K>::Node2 *n)
>
>在 gcc 是有能力處理的,也就是說在函數的參數 Tree<T,K>::Node2 被認知是一個 class,顯然編譯器是知道的,但為什麼作為
>回傳值又不知道了呢,我想是編譯器認為 Tree<T,K> 在外面和裡面可能不是相同的,也訧是說可能是如下:
 
修正這裡,因 template 的參數都一樣,所以不管裡面還是外面都相同,既然有能力處理裡面的,外面沒理由不能處理,所以 typename 是脫褲子放屁 ^^
作者 : sunyear(coco) VC++卓越專家C++頂尖高手貼文超過2000則
[ 貼文 2421 | 人氣 1485 | 評價 6060 | 評價/貼文 2.5 | 送出評價 5 次 ] 
[ 給個讚 ]  [ 給個讚 ]  [ 回應本文 ]  [ 發表新文 ]  [ 回上頁 ] [ 回討論區列表 ] [ 回知識入口 ]
2011/5/22 上午 02:27:12
>>先假設 Tree<T,K>::Node2 是 type name,那麼產生的填空框架
>>會是函式的定義。若是結果Tree<T,K>::Node2是個物件,那麼敘述
>>會變成表示式的框架和假設產生函式定義框架完全不搭。
>>結果會是個錯誤的分析。
>先假設是 type name 那麼這個框架就是 type name,也就是一個物件,怎麼會是一個函數呢
看原式子:
Tree<T,K>::Node2 *Tree<T,K>::Func2(Node2 *n);
右邊"Tree<T,K>::Func2(Node2 *n)"顯然是一個函式,當"Tree<T,K>::Node2"是type name時。
整個敘述框架會是:函式反回一個型態指標。//<--這是我的意思
當左邊"Tree<T,K>::Node2"是個物件時,整個敘述會被分析成一個運算式框架:
一個物件 乘以 函式(可作為運算式的operand)

>typename Tree<T,K>::Node2 *Tree<T,K>::Func2(Tree<T,K>::Node2 *n)
>在 gcc 是有能力處理的,也就是說在函數的參數 Tree<T,K>::Node2 被認知是一個 class,
>...我想是編譯器認為 Tree<T,K> 在外面和裡面可能不是相同的
應不是內外的關係。
如我前面的推測:gcc是因敘述的前已經認知了typename Tree<T,K>::Node2。
所以敘述之後的Tree<T,K>::Node2都會當typename處理。
或許你可以改成這樣試試我推測的對不對:
void Tree<T,K>::Func2(Tree<T,K>::Node2 *n)
看看gcc還認不認得參數Tree<T,K>::Node2是什麼?
無論如何,這只是猜測,可以確定的是,這不標準。
標準要求只要出現dependent type的地方就要加typename。

>Node2 絕不會是被識為 Tree<T>::Node2,若 T 是一個 class,
> Node2 是 T 中的一個 class 就讓它過關,若 T 不是 class 或 Node2
>不是 T 中的 class ,編譯器就丟出 error 吧,加不加 typename 都沒差吧
我只是在舉一個深度分析時,比想像中複雜。不做深度分析問題又回到原點。
不是class就丟出error?OK,舉一個丟出error也有問題的子。
template<typename T>
struct A
{
typedef T doubleID;//doubleID在structA中是一個型態
};
template<typename T>
struct B
{
static T doubleID;;//doubleID在structB中是一個物件

};
template<typename T> T B<T>::doubleID;

template<typename T>
struct C
{
public:
void Test()
{
T::doubleID *func();//T是A或B時,這個敘述會是完全不同的意思
}
};

void main(){
C<A<int> > AObj;
C<B<int> > BObj;
AObj.Test();
BObj.Test();
}

int * func(){return NULL;}
 
但注意到 T::doubleID *func(); 這個式子:
在 C<A<int> > AObj,他是當一個函數原型的宣告。
但在C<B<int> > BObj;時詭異的變成一個運算式。
儘管是在template中的敘述,敘述的抽象意含應該是一致的。
所以C<A<int> > AObj 和 C<B<int> > BObj應該要有一個是錯的。
編譯器應該要指出那一個是錯的呢?
T::doubleID不是class就丟出error?
但若程式員本意是一個運算式呢?

這個例子,在VS2003之前應該是會過關的。
T::doubleID *func()俱現出兩個完全不同的意思。
但這是錯的,敘述的抽象意含應該是一致的,即使在泛型設計。
所以VS新的版本糾正了這個錯。

作者 : sunyear(coco) VC++卓越專家C++頂尖高手貼文超過2000則
[ 貼文 2421 | 人氣 1485 | 評價 6060 | 評價/貼文 2.5 | 送出評價 5 次 ] 
[ 給個讚 ]  [ 給個讚 ]  [ 回應本文 ]  [ 發表新文 ]  [ 回上頁 ] [ 回討論區列表 ] [ 回知識入口 ]
2011/5/22 上午 02:30:54
我再一次重覆你的例子:
Tree<T,K>::Node2 *Tree<T,K>::Func2(Node2 *n)
因為引用的Node2就是當下俱現的模板Tree。所以會比較簡單的加以認知。
但在有些情況會變得很複雜,有的可以費九牛二虎之力克服,有的會是不可能的任務。
在這種情形下,一個語言就必需訂出一個標準讓大家遵循。
標準總不能訂出一大堆情況,然後說那幾種情況可以不加,那幾種情況一定要加。
這會把程式員搞死!
簡單的標準不是很好嗎?相依的型態,請加 typename。
有些編譯器,在它的"方便"下容許了較簡單可以判別的敘述,可以省略typename。
那是編譯器的選擇,要寫一個可攜性好的程式,就該依標準撰寫才是。
 
作者 : cxxlman(CxxlMan) C++優秀好手貼文超過1000則
[ 貼文 1042 | 人氣 3227 | 評價 1260 | 評價/貼文 1.21 | 送出評價 28 次 ] 
[ 給個讚 ]  [ 給個讚 ]  [ 回應本文 ]  [ 發表新文 ]  [ 回上頁 ] [ 回討論區列表 ] [ 回知識入口 ]
2011/5/22 下午 03:56:07

>看原式子:
>Tree<T,K>::Node2 *Tree<T,K>::Func2(Node2 *n);
>右邊'Tree<T,K>::Func2(Node2 *n)'顯然是一個函式,當'Tree<T,K>::Node2'是type name時。
>整個敘述框架會是:函式反回一個型態指標。//<--這是我的意思
>當左邊'Tree<T,K>::Node2'是個物件時,整個敘述會被分析成一個運算式框架:
>一個物件 乘以 函式(可作為運算式的operand)

只要編譯器知道 Node2 是什麼,就不會搞混了,不管編譯器是是藉由 typename 還是自已認知

>
>或許你可以改成這樣試試我推測的對不對:
>void Tree<T,K>::Func2(Tree<T,K>::Node2 *n)
>看看gcc還認不認得參數Tree<T,K>::Node2是什麼?
>無論如何,這只是猜測,可以確定的是,這不標準。
>標準要求只要出現dependent type的地方就要加typename。
 
剛試過了 gcc 可以 vs2008 不行

>
>>Node2 絕不會是被識為 Tree<T>::Node2,若 T 是一個 class,
>> Node2 是 T 中的一個 class 就讓它過關,若 T 不是 class 或 Node2
>>不是 T 中的 class ,編譯器就丟出 error 吧,加不加 typename 都沒差吧
>我只是在舉一個深度分析時,比想像中複雜。不做深度分析問題又回到原點。
>不是class就丟出error?OK,舉一個丟出error也有問題的子。
>template<typename T>
>struct C
>{
>public:
> void Test()
> {
> T::doubleID *func();//T是A或B時,這個敘述會是完全不同的意思
> }
>};
>
>int * func(){return NULL;}

我知你要表達的意思,只是你這辛苦做出的例子沒能成功, 因func() 回覆的是 int * 是一個指標,指標對 B 的乘法運算無效,若改回覆的是 int 對 A 又無效了

作者 : cxxlman(CxxlMan) C++優秀好手貼文超過1000則
[ 貼文 1042 | 人氣 3227 | 評價 1260 | 評價/貼文 1.21 | 送出評價 28 次 ] 
[ 給個讚 ]  [ 給個讚 ]  [ 回應本文 ]  [ 發表新文 ]  [ 回上頁 ] [ 回討論區列表 ] [ 回知識入口 ]
2011/5/22 下午 04:09:11

>我再一次重覆你的例子:
>Tree<T,K>::Node2 *Tree<T,K>::Func2(Node2 *n)
>因為引用的Node2就是當下俱現的模板Tree。所以會比較簡單的加以認知。
>但在有些情況會變得很複雜,有的可以費九牛二虎之力克服,有的會是不可能的任務。
>在這種情形下,一個語言就必需訂出一個標準讓大家遵循。
>標準總不能訂出一大堆情況,然後說那幾種情況可以不加,那幾種情況一定要加。
>這會把程式員搞死!
>簡單的標準不是很好嗎?相依的型態,請加 typename。
>有些編譯器,在它的'方便'下容許了較簡單可以判別的敘述,可以省略typename。
>那是編譯器的選擇,要寫一個可攜性好的程式,就該依標準撰寫才是。

加上 typename 的意思和我提到先假設是 type name 功效是一樣的,就是在編譯前先置放 type name 框架,由編譯器自己加 typename 省事多了,只是這麼做會不會有例外,也就是你當心的乘法問題,所以對 template class 做前置處理還是比較好,至於性能就由編譯器的開發者去傷腦筋了
作者 : sunyear(coco) VC++卓越專家C++頂尖高手貼文超過2000則
[ 貼文 2421 | 人氣 1485 | 評價 6060 | 評價/貼文 2.5 | 送出評價 5 次 ] 
[ 給個讚 ]  [ 給個讚 ]  [ 回應本文 ]  [ 發表新文 ]  [ 回上頁 ] [ 回討論區列表 ] [ 回知識入口 ]
2011/5/22 下午 08:20:59
>我知你要表達的意思,只是你這辛苦做出的例子沒能成功,
> 因func() 回覆的是 int * 是一個指標,指標對 B 的乘法運算無效,
>若改回覆的是 int 對 A 又無效了
不,你沒能理解我要表達的意思。
我舉的例子不再顯示一個template可以產生兩個合理可以運作的程式碼。
而是在於,這個例子若不加typename足以困擾編譯器,決定該敘述到底是什麼框架。

>加上 typename 的意思和我提到先假設是 type name 功效是一樣的,...
不一樣。
標準C++要求dependent type 要加typename的意思,已經暗示,編譯器對於
未加typename的denpendent name預設會是object。
沒依照這個要求的編譯器,或許對很多人會感到方便,但會在某些情況編譯出錯。

>由編譯器自己加 typename 省事多了,只是這麼做會不會有例外,也就是你當心的乘法問題
不只是乘法的問題。像(dependent name)到底是cast還是一個operand括號而已。
還有其它我也不知道的種種問題比想像的多。
我相信制定C++標準的委員先生,比我們都更深入研究過。對這個議題我只能依有限的認知
提出看法。有些標準,我也很懷疑,但基本上我還是相信委員們比我更有學問^^
舉個例子,C++有一個形成中的concept關鍵字,它可用來取代模板的class和typename。
例如: Tree<concept T>.....
由於concept對T有更詳盡的抽象定義,所以我覺得有機會讓模板特製化成為過去。
它也可以對你例子的Node2預作定義,使得我們討論半天議題也可能消失。
本來我對它有些期待,但突然聽說concept在C++ 0.x中,被取消了。
雖然覺得很可惜,但我相信做這個決定的委員有有它們正當的理由。
如果有一天,concept在0.x之後的版本又復活了,我也不訝異。
本來取消的理由消失或獲得解決,concept就會再被提出。
作者 : sunyear(coco) VC++卓越專家C++頂尖高手貼文超過2000則
[ 貼文 2421 | 人氣 1485 | 評價 6060 | 評價/貼文 2.5 | 送出評價 5 次 ] 
[ 給個讚 ]  [ 給個讚 ]  [ 回應本文 ]  [ 發表新文 ]  [ 回上頁 ] [ 回討論區列表 ] [ 回知識入口 ]
2011/5/22 下午 08:23:43
訂正:
>標準C++要求dependent type 要加typename的意思,已經暗示,編譯器對於
>未加typename的denpendent name預設會是object。
...
未加typename的denpendent name會被當成object。
作者 : cxxlman(CxxlMan) C++優秀好手貼文超過1000則
[ 貼文 1042 | 人氣 3227 | 評價 1260 | 評價/貼文 1.21 | 送出評價 28 次 ] 
[ 給個讚 ]  [ 給個讚 ]  [ 回應本文 ]  [ 發表新文 ]  [ 回上頁 ] [ 回討論區列表 ] [ 回知識入口 ]
2011/5/22 下午 09:23:23

>不,你沒能理解我要表達的意思。
>我舉的例子不再顯示一個template可以產生兩個合理可以運作的程式碼。
>而是在於,這個例子若不加typename足以困擾編譯器,決定該敘述到底是什麼框架。

沒困擾到編譯器啊,int *func() A 可行 B 不行, int func() 時 A 不行 B 可行,編譯器分的很清楚,我在 vs2008 試過了

>
>>加上 typename 的意思和我提到先假設是 type name 功效是一樣的,...
>不一樣。
>標準C++要求dependent type 要加typename的意思,已經暗示,編譯器對於
>未加typename的denpendent name預設會是object。
>沒依照這個要求的編譯器,或許對很多人會感到方便,但會在某些情況編譯出錯。

這怎可能會是用預設,是 Object 可能要用編譯成乘法的指令,否則就要視為其他功能,編譯器對運算子和運算元之間要能清楚明白才會做出正確的動作,否則會丟出摸擬兩可的 error

>
>>由編譯器自己加 typename 省事多了,只是這麼做會不會有例外,也就是你當心的乘法問題
>不只是乘法的問題。像(dependent name)到底是cast還是一個operand括號而已。
>還有其它我也不知道的種種問題比想像的多。
>我相信制定C++標準的委員先生,比我們都更深入研究過。對這個議題我只能依有限的認知
>提出看法。有些標準,我也很懷疑,但基本上我還是相信委員們比我更有學問^^
>舉個例子,C++有一個形成中的concept關鍵字,它可用來取代模板的class和typename。
>例如: Tree<concept T>.....
>由於concept對T有更詳盡的抽象定義,所以我覺得有機會讓模板特製化成為過去。
>它也可以對你例子的Node2預作定義,使得我們討論半天議題也可能消失。
>本來我對它有些期待,但突然聽說concept在C++ 0.x中,被取消了。
>雖然覺得很可惜,但我相信做這個決定的委員有有它們正當的理由。
>如果有一天,concept在0.x之後的版本又復活了,我也不訝異。
>本來取消的理由消失或獲得解決,concept就會再被提出。

其實我蠻希望 C++ 有辦法提供 Multimethod 的功能,不過這樣扯遠了,其實一開始我只是對 typename 用在 class 的成員函數的宣告上覺得多此一舉而已
作者 : sunyear(coco) VC++卓越專家C++頂尖高手貼文超過2000則
[ 貼文 2421 | 人氣 1485 | 評價 6060 | 評價/貼文 2.5 | 送出評價 5 次 ] 
[ 給個讚 ]  [ 給個讚 ]  [ 回應本文 ]  [ 發表新文 ]  [ 回上頁 ] [ 回討論區列表 ] [ 回知識入口 ]
2011/5/23 上午 12:50:13
>沒困擾到編譯器啊,int *func() A 可行 B 不行, int func() 時 A 不行 B 可行,
>編譯器分的很清楚,我在 vs2008 試過了
某個編譯器過不過,不是我們討論的重點,不是嗎?
如果你明白我的意思,反而是要把程式式改成A和B都會出錯的情況,好從錯誤訊息
判斷到底VS208在A和B的情況,把T::doubleID *func();當成什麼才是。

討論的重點在於不加typename,編譯器對T::doubleID *func();這敘述要怎麼解釋。
AObj.Test();沒有錯誤訊息作佐證,但應可以同意vs2008把T::doubleID *func();這行視為一個"宣告"
至於BObj.Test();編譯通常會出現"The right operand used with operator is invalid."的錯誤訊息。
這不正是vs2008把T::doubleID *func();當成"運算式"的明證嗎?(不是運算式那來operand)
即使在template,一個敘述不能是"宣告",又是"運算式"的兩用框架。
否則泛型設計就變成泛濫設計了。
語言的設計,應是要讓parser可以明確的辨識一個敘述到底是"宣告"還是"運算式"。

>其實一開始我只是對 typename 用在 class 的成員函數的宣告上覺得多此一舉而已
typenamem 一詞是在template使用一段時間後才發明的東西。相依型態要冠上typename
又是之後才決定的。如果所延生的問題,原規則可以解決,制訂委員應是不會多此一舉。
討論已夠多了,如果你還是覺得相依型態,可以不必冠上typename。
就努力的成為C++標準的制訂委員,或許你可以說服其他委員。
取消 typename 成為標準^^
作者 : cxxlman(CxxlMan) C++優秀好手貼文超過1000則
[ 貼文 1042 | 人氣 3227 | 評價 1260 | 評價/貼文 1.21 | 送出評價 28 次 ] 
[ 給個讚 ]  [ 給個讚 ]  [ 回應本文 ]  [ 發表新文 ]  [ 回上頁 ] [ 回討論區列表 ] [ 回知識入口 ]
2011/5/23 上午 01:55:04

>>沒困擾到編譯器啊,int *func() A 可行 B 不行, int func() 時 A 不行 B 可行,
>>編譯器分的很清楚,我在 vs2008 試過了
>某個編譯器過不過,不是我們討論的重點,不是嗎?
>如果你明白我的意思,反而是要把程式式改成A和B都會出錯的情況,好從錯誤訊息
>判斷到底VS208在A和B的情況,把T::doubleID *func();當成什麼才是。
>
>討論的重點在於不加typename,編譯器對T::doubleID *func();這敘述要怎麼解釋。
>AObj.Test();沒有錯誤訊息作佐證,但應可以同意vs2008把T::doubleID *func();這行視為一個'宣告'
>至於BObj.Test();編譯通常會出現'The right operand used with operator is invalid.'的錯誤訊息。
>這不正是vs2008把T::doubleID *func();當成'運算式'的明證嗎?(不是運算式那來operand)
>即使在template,一個敘述不能是'宣告',又是'運算式'的兩用框架。
>否則泛型設計就變成泛濫設計了。
>語言的設計,應是要讓parser可以明確的辨識一個敘述到底是'宣告'還是'運算式'。

你誤解了 T::doubleID *func(); 會有宣告'還是'運算式'的疑慮,其實是不會的,把 BObj.Test(); 展開來其實它的意思如下

int *I = func(); // int *func(); 是這樣宣告的
int doubleID;
doubleID * i; // int * (int*) 數值乘位址不合法

所以絕不可能有"運算式'的情形出現


>>其實一開始我只是對 typename 用在 class 的成員函數的宣告上覺得多此一舉而已
>typenamem 一詞是在template使用一段時間後才發明的東西。相依型態要冠上typename
>又是之後才決定的。如果所延生的問題,原規則可以解決,制訂委員應是不會多此一舉。
>討論已夠多了,如果你還是覺得相依型態,可以不必冠上typename。
>就努力的成為C++標準的制訂委員,或許你可以說服其他委員。
>取消 typename 成為標準^^
>
會用這種特別聲明大概有四種情況:1. 模擬兩可時 2. 強制轉形時 3. 引用到尚未明白的形別時 4. 多此一舉
只是不知 typename 屬於哪一種而已


作者 : sunyear(coco) VC++卓越專家C++頂尖高手貼文超過2000則
[ 貼文 2421 | 人氣 1485 | 評價 6060 | 評價/貼文 2.5 | 送出評價 5 次 ] 
[ 給個讚 ]  [ 給個讚 ]  [ 回應本文 ]  [ 發表新文 ]  [ 回上頁 ] [ 回討論區列表 ] [ 回知識入口 ]
2011/5/23 上午 10:17:32
>你誤解了 T::doubleID *func(); 會有宣告'還是'運算式'的疑慮,其實是不會的,把 BObj.Test(); 展開來其實它的意思如下
>int *I = func(); // int *func(); 是這樣宣告的
>int doubleID;
>doubleID * i; // int * (int*) 數值乘位址不合法
>所以絕不可能有"運算式'的情形出現
"所以絕不可能有"運算式'的情形出現"?你的分析就是把它當運算式在解讀,不管運算式語法對不對。
這樣的分析是程式合不合理或對不對的思考,不是語法解析的技術問題。
這根本不在制訂C++標準的範圍!
C++標準在舉例說明為什麼需要typename,通常只會給到
T::doubleID LocalOrGlobal;//<--T::doubleID可能為型態或物件的岐義
這已經足夠說明不加typename,在語法分析的困擾了。
至於編譯器會不會有奇奇怪怪的錯誤檢知已經不是重點。

我的例子寫多了一點,算是多此一舉,反而令討論失了焦 :(
你要一個可以通過編譯的雙重身份的T::doubleID,小改一下:
int LocalOrGlobal;
template<typename T>
struct A
{
typedef T doubleID;
};
template<typename T>
struct B
{
static T doubleID;
};
template<typename T> T B<T>::doubleID;

template<typename T>
class C
{
public:
void Test()
{
T::doubleID *LocalOrGlobal;
LocalOrGlobal=0;
}
};

void main(){
C<A<int> > AObj;
C<B<int> > BObj;
AObj.Test();
BObj.Test();
}

這可以避開operand是個指標的問題。
或許某些編譯器會出現"operator has no effect"的訊息。
但這只是個警告,不是錯誤。警告只是提醒程式師,該運算式
沒有作用,也可能會在最佳化的過程被忽略(不產生程式碼)。
若,你還要一個完美無缺(連個warning都沒有)的範例可以"騙"過你的編譯器,
可能得對目標編譯器量身訂制了。我也不想去安裝和目前工作無關的編譯器,
所以有待你自己去試試。最主要的還是因為和原問題的討論無關了。
作者 : sunyear(coco) VC++卓越專家C++頂尖高手貼文超過2000則
[ 貼文 2421 | 人氣 1485 | 評價 6060 | 評價/貼文 2.5 | 送出評價 5 次 ] 
[ 給個讚 ]  [ 給個讚 ]  [ 回應本文 ]  [ 發表新文 ]  [ 回上頁 ] [ 回討論區列表 ] [ 回知識入口 ]
2011/5/23 下午 12:14:58
雖然有點失焦,但 cxxlman喜歡從編譯得過的程式理解,就再舉一個
允許省略typename的編譯器可以過關,但明顯是一個岐異的程式碼。

int Arg;
template<typename T> struct A
{
static int &x(int i){
static int My;
return (My+=i);
}
};
template<typename T> struct B
{
typedef int x;
};
template<typename T>
void func()
{
T::x(Arg)=0;//(這裡是焦點)以下稱為X行
}


int _tmain(int argc, _TCHAR* argv[])
{
func<A<int> >();//以下稱A行
func<B<int> >();//以下稱B行
return 0;
}
//===================================

X行到底是宣告local Arg變數,還是函式呼叫?
依C++的標準,沒有岐異,T::x沒有加typename,所以是函式呼叫。
A行OK,B行編譯時應NG,產生錯誤訊息。
若是允許相依型態可以不加typename的編譯器,編譯應可以過關,
但可能編譯的結果是:
1.A行和B行都令X行產生函式呼叫的程式碼,或宣告敘述。
2.A行令X行生函式呼叫程式碼,B行則令X行解譯成宣告local變數。
也許有人會覺得2.也不錯,一魚兩吃。
但這不符泛型設計,而是泛濫設計,是失控的程式碼。
作者 : cxxlman(CxxlMan) C++優秀好手貼文超過1000則
[ 貼文 1042 | 人氣 3227 | 評價 1260 | 評價/貼文 1.21 | 送出評價 28 次 ] 
[ 給個讚 ]  [ 給個讚 ]  [ 回應本文 ]  [ 發表新文 ]  [ 回上頁 ] [ 回討論區列表 ] [ 回知識入口 ]
2011/5/23 下午 01:57:58
以上兩個例子你都只是把 template class 當成 macro 在用,能不能編譯通過和 typename 都無關,你應該創造一個模擬兩可的情況須要 typename 的幫忙才能看出 typename 價值

你以上的兩個例子都正確,只是因參數不同展開不同的程式碼而已
作者 : sunyear(coco) VC++卓越專家C++頂尖高手貼文超過2000則
[ 貼文 2421 | 人氣 1485 | 評價 6060 | 評價/貼文 2.5 | 送出評價 5 次 ] 
[ 給個讚 ]  [ 給個讚 ]  [ 回應本文 ]  [ 發表新文 ]  [ 回上頁 ] [ 回討論區列表 ] [ 回知識入口 ]
2011/5/23 下午 03:37:01
>以上兩個例子你都只是把 template class 當成 macro 在用,
>能不能編譯通過和 typename 都無關,
>你以上的兩個例子都正確,只是因參數不同展開不同的程式碼而已
只需要把struct A,B多加一些成員進去,就不是macro可以做到的了。
但你又會說,但X行你只用某個class類似macro可以做到的元素。
然後我又得修改....這樣子有意義嗎?
我用的是template的語法,已造成語意上的分岐,而且可以讓所謂不標準
的編譯器過關,這樣還不夠嗎?
一個template內的敘述該是宣告就是宣告,是運算式就是運算式。兩種都可能就是模擬兩可。
(macro可以允許,template不能允許)
前面那個你最不滿意的例子來說,運算式的語法有錯,它還是被當運算式(錯誤的運算式)
。除非編譯器做到運算式語法有錯時,再回頭不把它當運算式再編譯看看(我沒見過這樣的編譯器)。
////
我所知道的程式語言,都是只要是名稱就得在該敘述的parse下決定它是什麼():
keyword, macro, 變數(物件)還是自定義型態...這樣該敘述的解釋框架才能成形。
該敘述都是以最初的決定去解析(不會碰到什麼錯又回頭改名稱的屬性),
這樣敘述才不會有過多的岐義。語言本身也才能嚴謹。編譯器也才能做得可靠。
所以當C++標準這麼說:
template<typename T>
class C{
T::X a;//<--T::X可能是型能或物件,所以若是型態必需冠上typename
};
這理由已足夠,不必再窮追省略了typename,編譯器是否需要typename幫忙才能
找出T::X到底是型態還是物件。
////
注意到兩個////間的說明,那才是的重點。不是我寫的那幾個例子(那只能作為延伸研究)

>你應該創造一個模擬兩可的情況須要 typename 的幫忙才能看出 typename 價值
我已經做了,不是嗎?(雖然我覺得沒必要)
你不能說它可以用macro做到一樣的功能(情況),就說它不是template
你用C++寫一個物件導向的程式,我說用C也可以寫出同樣的功能,
所以你寫的不是物件導向的程式,那你能接受嗎?
那用assembler也可以寫出C同樣功能的程式,那我寫的C也不是C了^^
作者 : cxxlman(CxxlMan) C++優秀好手貼文超過1000則
[ 貼文 1042 | 人氣 3227 | 評價 1260 | 評價/貼文 1.21 | 送出評價 28 次 ] 
[ 給個讚 ]  [ 給個讚 ]  [ 回應本文 ]  [ 發表新文 ]  [ 回上頁 ] [ 回討論區列表 ] [ 回知識入口 ]
2011/5/23 下午 06:17:52
不想再談 typename 了,雖然是個標準,但和編譯器的關係太密切,使用到的機會又很少

就說說你那例子為什麼是不是模擬兩可,模擬兩可的樣子應該像這樣 A * B ,很像是 A 乘以 B ,也像是 B 是 A 的指標,在你的 template 為具現前是很像,但具現展開後完全另一副模樣,所以編譯器並不會搞不淸楚
作者 : cxxlman(CxxlMan) C++優秀好手貼文超過1000則
[ 貼文 1042 | 人氣 3227 | 評價 1260 | 評價/貼文 1.21 | 送出評價 28 次 ] 
[ 給個讚 ]  [ 給個讚 ]  [ 回應本文 ]  [ 發表新文 ]  [ 回上頁 ] [ 回討論區列表 ] [ 回知識入口 ]
2011/5/24 上午 01:58:36

>int LocalOrGlobal;
>template<typename T>
>struct A
>{
> typedef T doubleID;
>};
>template<typename T>
>struct B
>{
> static T doubleID;
>};
>template<typename T> T B<T>::doubleID;
>
>template<typename T>
>class C
>{
>public:
> void Test()
> {
> T::doubleID *LocalOrGlobal;
> LocalOrGlobal=0;
> }
>};
>
>void main(){
> C<A<int> > AObj;
> C<B<int> > BObj;
> AObj.Test();
> BObj.Test();
>}

把你這一段分別是用 vs2008 和 gcc 4.5.0 測試過,vs2008 完全沒問題,gcc 須為 AObj 在 T::doubleID *LocalOrGlobal;
 在前頭多加 typename

這測試表示 vs2008 有能力不須為 parse 1 多加 typename,gcc 就沒能力了嗎?不見得,前頭我弄出來例子可以這樣做void Tree<T,K>::Func2(Tree<T,K>::Node2 *n),在 vs2008 不行,在 gcc 反而可以

因此我認為以目前編譯器都會想去符合標準規定,但又不自覺的把自己的能力透露出來,正好證明 typename 的標準真的是多餘的,建議 coco 去向標準制定單位建議,把 typename 這個不合時宜的東西砍了^^

 
作者 : sunyear(coco) VC++卓越專家C++頂尖高手貼文超過2000則
[ 貼文 2421 | 人氣 1485 | 評價 6060 | 評價/貼文 2.5 | 送出評價 5 次 ] 
[ 給個讚 ]  [ 給個讚 ]  [ 回應本文 ]  [ 發表新文 ]  [ 回上頁 ] [ 回討論區列表 ] [ 回知識入口 ]
2011/5/24 上午 10:19:19
>把你這一段分別是用 vs2008 和 gcc 4.5.0 測試過,vs2008 完全沒問題,
>:::::::::::::::::::這測試表示 vs2008 有能力不須為 parse 1 多加 typename,
>gcc 就沒能力了嗎?:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
這不表示VS2008比較有能力。
有些觀念我一再重覆提,但似乎你聽不進去。
把一個"有問題"的程式,編譯過也可以產生一個程式,相反的表示這個編譯器是有問題的。

請至少把這句聽進去
****模板裡面的敘述只能代表一個抽象意涵,否則就變成有板無模。******

T::doubleID *LocalOrGlobal;//<--這個敘述,只能代表一個抽象意涵:宣告或運算式
程式員撰寫這行程式,只會(也只能)有一個意思:宣告一個物件,或者他試圖寫一個運算式。
因為A,B 有不同屬性的名稱的重疊,加陰陽際會,使得T::doubleID *LocalOrGlobal出現了兩個框架。
好的編譯器是應該指出這個岐異,而不是閉著眼睛,產生一個非設計者本意的程式碼。
要指出這個岐異,編譯器不能當法官決定那一個框架是程式員的意思(即使某個框架在語法上有錯)。
正確的做法就是,程式師明確的指出他想要做什麼:
typename T::doubleID *LocalOrGlobal;//宣告物件的敘述
T::doubleID *LocalOrGlobal;//這是個運算式
這使得語言嚴謹化,降低雞(程式員)同鴨(編譯器)講的情況。

>目前編譯器都會想去符合標準規定,但又不自覺的把自己的能力透露出來
沒錯,VS是領導品牌。
但,寫程式不應依賴編譯器的特異功能,而是儘量按標準去寫。
這不只是可攜性的問題,也是要避免雞同鴨講。
雖然我自己也中了VS的毒,不自覺的使用VS的特異功能,用起來似乎也沒問題。
但我不能說我這樣子是對的。
作者 : cxxlman(CxxlMan) C++優秀好手貼文超過1000則
[ 貼文 1042 | 人氣 3227 | 評價 1260 | 評價/貼文 1.21 | 送出評價 28 次 ] 
[ 給個讚 ]  [ 給個讚 ]  [ 回應本文 ]  [ 發表新文 ]  [ 回上頁 ] [ 回討論區列表 ] [ 回知識入口 ]
2011/5/24 下午 01:30:30
所以現在是否應該保留 typename 有兩個考量方向

1. 能力問題(你不喜歡能力這兩個字自己換吧),這一點應不用懷疑,也就是說假設 typename 不再視為標準,編譯器應可不費吹灰之力改掉

2. 意圖問題,你希望在寫下一段程式語句就要用 typername "表明"它的意圖,以免被誤用,這點我同意,但首先要把 typername 的功能先"表明"清楚,事實上 typername 的適用(應說必須使用)範圍比這要廣,即使沒有意圖疑慮的地方也得加上,我認為若是為了 parse 考量的部份要先排除,以正視聽
作者 : cxxlman(CxxlMan) C++優秀好手貼文超過1000則
[ 貼文 1042 | 人氣 3227 | 評價 1260 | 評價/貼文 1.21 | 送出評價 28 次 ] 
[ 給個讚 ]  [ 給個讚 ]  [ 回應本文 ]  [ 發表新文 ]  [ 回上頁 ] [ 回討論區列表 ] [ 回知識入口 ]
2011/5/24 下午 02:50:01

>2. 意圖問題,你希望在寫下一段程式語句就要用 typername '表明'它的意圖,以免被誤用,這點我同意,但首先要把 typername 的
>功能先'表明'清楚,事實上 typername 的適用(應說必須使用)範圍比這要廣,即使沒有意圖疑慮的地方也得加上,我認為若是為了
>parse 考量的部份要先排除,以正視聽

其實這點也不用擔心,就像張嘴想要 "喝湯" 還是 "吃飯" 不用先聲明,看動手拿的是哪一樣,萬一喝湯後還要嚼幾口,就表示拿錯了
作者 : sunyear(coco) VC++卓越專家C++頂尖高手貼文超過2000則
[ 貼文 2421 | 人氣 1485 | 評價 6060 | 評價/貼文 2.5 | 送出評價 5 次 ] 
[ 給個讚 ]  [ 給個讚 ]  [ 回應本文 ]  [ 發表新文 ]  [ 回上頁 ] [ 回討論區列表 ] [ 回知識入口 ]
2011/5/24 下午 03:00:48
>所以現在是否應該保留 typename 有兩個考量方向
>1. 能力問題(你不喜歡能力這兩個字自己換吧),這一點應不用懷疑,
>也就是說假設 typename 不再視為標準,編譯器應可不費吹灰之力改掉
>2. 意圖問題,你希望在寫下一段程式語句就要用 typername "表明"它的意圖,
>以免被誤用,這點我同意,但首先要把 typername 的功能先"表明"清楚,

改成有犯錯能力吧!
前面的子:
int Arg;
template<typename T> struct A
{
static int &x(int i){
static int My;
return (My+=i);
}
};
template<typename T> struct B
{
typedef int x;
};
template<typename T>
void func()
{
T::x(Arg)=0;//(這裡是焦點)以下稱為X行
}
int _tmain(int argc, _TCHAR* argv[])
{
func<A<int> >();//以下稱A行
func<B<int> >();//以下稱B行
return 0;
}
我在VC6和VS2008試過(剛好碰到一台有2008版的),結果 ,
VC6:
void main()
{
func<A<int> >();//X行(T::x(Arg)=0;)執行的是函式呼叫
//func<B<int> >();//本行comment,不執行
return 0;
}
然後小改一下再試
void main()
{
func<A<int> >();//X行執行的是宣告Arg物件,並初始化為0<==注意這裡的改變
func<B<int> >();//X行執行的是宣告Arg物件,並初始化為0
return 0;
}
看到兩個A行(func<A<int> >();)的差異嗎?
同樣的func<A<int> >();被編成不同的程式碼,一個荒誕的錯誤。
這是很有能力還是有犯錯的能力?

試試VS2008有沒有改善(號稱已符合C++對typename 要求的標準):
void main()
{
func<A<int> >();//X行(T::x(Arg)=0;)執行的是函式呼叫
func<B<int> >();//X行執行的是宣告Arg物件,並初始化為0
return 0;
}
這個看起來比較正常一點,但,問題在於X行(T::x(Arg)=0;)被解釋成
兩種截然不同的框架(一個是宣告,一個是函式呼叫)
不論是VC還是VS2008都是錯的!
typename 能省嗎?在這個例子,省不了!
加上typename 在這裡不是只為表明程式員的意圖而已,而是必需的。
(否則編譯器無法判別X行到底是宣告還是函式呼叫)

>事實上 typername 的適用(應說必須使用)範圍比這要廣,即使沒有意圖疑慮的地方也得加上,
>我認為若是為了 parse 考量的部份要先排除,以正視聽
你可以試著整理出所有使用typename的敘述,然後在它們裡面劃出一條線,分出那些是該加,
那些可以不加。要找出所有可能parse沒問題的部份都可省略,基本上困難重重,
就算成功也會把程式員搞得暈頭轉向。
(注意所謂parse沒問題,不是說可以編譯過,而是沒有謬誤。能夠"正確"指出程式謬誤也算是parse沒問題)
目前標準是有這條線:基類別相依型態可以不加typename,其它相依型態要加。
你可以試著移動這條線。
作者 : sunyear(coco) VC++卓越專家C++頂尖高手貼文超過2000則
[ 貼文 2421 | 人氣 1485 | 評價 6060 | 評價/貼文 2.5 | 送出評價 5 次 ] 
[ 給個讚 ]  [ 給個讚 ]  [ 回應本文 ]  [ 發表新文 ]  [ 回上頁 ] [ 回討論區列表 ] [ 回知識入口 ]
2011/5/24 下午 03:12:34
我先幫你把那條線移動一點點:
模版中使用巢狀相依型態時,除下面情況外,需加typename:
1.基類別相依型態。
2.相依型態是在巢狀定義的上一層模版類別中定義。
第2.點是應你最先的例子而訂的。已經有點咬文嚼字。
越往下會越難敘述,爭議性也會越大。
作者 : cxxlman(CxxlMan) C++優秀好手貼文超過1000則
[ 貼文 1042 | 人氣 3227 | 評價 1260 | 評價/貼文 1.21 | 送出評價 28 次 ] 
[ 給個讚 ]  [ 給個讚 ]  [ 回應本文 ]  [ 發表新文 ]  [ 回上頁 ] [ 回討論區列表 ] [ 回知識入口 ]
2011/5/24 下午 05:05:25

>看到兩個A行(func<A<int> >();)的差異嗎?
>同樣的func<A<int> >();被編成不同的程式碼,一個荒誕的錯誤。
>這是很有能力還是有犯錯的能力?
  
  你指的錯誤是指違背程式設計師意圖吧,當程式設計師把想法寫成程式後,有沒有違背要由程式來決定
 
>
>試試VS2008有沒有改善(號稱已符合C++對typename 要求的標準):
>void main()
>{
>func<A<int> >();//X行(T::x(Arg)=0;)執行的是函式呼叫
>func<B<int> >();//X行執行的是宣告Arg物件,並初始化為0
>return 0;
>}
>這個看起來比較正常一點,但,問題在於X行(T::x(Arg)=0;)被解釋成
>兩種截然不同的框架(一個是宣告,一個是函式呼叫)
>不論是VC還是VS2008都是錯的!
>typename 能省嗎?在這個例子,省不了!
>加上typename 在這裡不是只為表明程式員的意圖而已,而是必需的。
>(否則編譯器無法判別X行到底是宣告還是函式呼叫)

如果兩者都能成立,那麼兩者都正確的,接下來就由後續來決定

>
>>事實上 typername 的適用(應說必須使用)範圍比這要廣,即使沒有意圖疑慮的地方也得加上,
>>我認為若是為了 parse 考量的部份要先排除,以正視聽
>你可以試著整理出所有使用typename的敘述,然後在它們裡面劃出一條線,分出那些是該加,
>那些可以不加。要找出所有可能parse沒問題的部份都可省略,基本上困難重重,
>就算成功也會把程式員搞得暈頭轉向。
>(注意所謂parse沒問題,不是說可以編譯過,而是沒有謬誤。能夠'正確'指出程式謬誤也算是parse沒問題)
>目前標準是有這條線:基類別相依型態可以不加typename,其它相依型態要加。
>你可以試著移動這條線。

就算加了 typename 也是會有問題,因 typename 可代表一般形別和 class ,若你要的的是一般形別結果卻給了 claas 怎麼辦,若你要的整數,卻用了浮點怎麼辦,還不是得由後續的程式碼來決定用的是否正確
作者 : cxxlman(CxxlMan) C++優秀好手貼文超過1000則
[ 貼文 1042 | 人氣 3227 | 評價 1260 | 評價/貼文 1.21 | 送出評價 28 次 ] 
[ 給個讚 ]  [ 給個讚 ]  [ 回應本文 ]  [ 發表新文 ]  [ 回上頁 ] [ 回討論區列表 ] [ 回知識入口 ]
2011/5/24 下午 05:08:44

>我先幫你把那條線移動一點點:
>模版中使用巢狀相依型態時,除下面情況外,需加typename:
>1.基類別相依型態。
>2.相依型態是在巢狀定義的上一層模版類別中定義。
>第2.點是應你最先的例子而訂的。已經有點咬文嚼字。
>越往下會越難敘述,爭議性也會越大。

typename 這麼麻煩不如砍了比較清心 ^^
作者 : sunyear(coco) VC++卓越專家C++頂尖高手貼文超過2000則
[ 貼文 2421 | 人氣 1485 | 評價 6060 | 評價/貼文 2.5 | 送出評價 5 次 ] 
[ 給個讚 ]  [ 給個讚 ]  [ 回應本文 ]  [ 發表新文 ]  [ 回上頁 ] [ 回討論區列表 ] [ 回知識入口 ]
2011/5/24 下午 08:48:39
>你指的錯誤是指違背程式設計師意圖吧,當程式設計師把想法寫成程式後,
>有沒有違背要由程式來決定
語法的計設計原則,要令編譯器可以合理的做出來。
編譯器的設計原則,要在合理範圍內,找出語法規則錯誤來。
(邏輯錯誤,就得由程式員自己debug)
如果依你的原則來設計編譯器,不管程式員怎麼寫程式,都可以產生程式碼。
(就算把一封情書交給編譯器,也可以產生程式碼)
這樣子,編譯不可能會有錯誤訊息,連結也不會。你是這樣的意思嗎?
但可能也沒人能寫一個像樣的程式(人太會犯錯了)
所以,我可能放棄寫程式這條路。^^

>就算加了 typename 也是會有問題,因 typename 可代表一般形別和 class
>若你要的的是一般形別結果卻給了 claas 怎麼辦,若你要的整數,
>卻用了浮點怎麼辦,還不是得由後續的程式碼來決定用的是否正確
沒說編譯器可以找到所有程式的錯誤。只能在合理的範圍。

>typename 這麼麻煩不如砍了比較清心 ^^
C++煩人的不只是typename吧!
從C以來,就有很多人生氣的發毒誓,此生永不再碰C。
這些人不但還在寫C,有的也在寫C++。
不過毒誓變成,寧願寫C毀誓,也不想碰C++。
 這些人,目前還是在寫C++。
如果舉辦個最像魔鬼的程式語言選拔,C++大概會中選;
但如果是最像天使的程式語言選拔,應還是C++中選。

作者 : sunyear(coco) VC++卓越專家C++頂尖高手貼文超過2000則
[ 貼文 2421 | 人氣 1485 | 評價 6060 | 評價/貼文 2.5 | 送出評價 5 次 ] 
[ 給個讚 ]  [ 給個讚 ]  [ 回應本文 ]  [ 發表新文 ]  [ 回上頁 ] [ 回討論區列表 ] [ 回知識入口 ]
2011/5/24 下午 08:51:11
>如果兩者都能成立,那麼兩者都正確的,接下來就由後續來決定
那首先你得重新定義模板。讓它一個敘述可以代表不同的抽象意函。
就我的認知,那叫有板無模。所以應不能叫模板了。
把模板砍了,另創名詞吧。不..也不需要了,macro就合乎要求。
那是一種單純的取代,取代完變成什麼就是什麼。
作者 : cxxlman(CxxlMan) C++優秀好手貼文超過1000則
[ 貼文 1042 | 人氣 3227 | 評價 1260 | 評價/貼文 1.21 | 送出評價 28 次 ] 
[ 給個讚 ]  [ 給個讚 ]  [ 回應本文 ]  [ 發表新文 ]  [ 回上頁 ] [ 回討論區列表 ] [ 回知識入口 ]
2011/5/25 下午 11:15:10

>>如果兩者都能成立,那麼兩者都正確的,接下來就由後續來決定
>那首先你得重新定義模板。讓它一個敘述可以代表不同的抽象意函。
>就我的認知,那叫有板無模。所以應不能叫模板了。
>把模板砍了,另創名詞吧。不..也不需要了,macro就合乎要求。
>那是一種單純的取代,取代完變成什麼就是什麼。

template class 的功能或者說目的本來就是把 template 參數套用上去,能讓整個 template class 處處皆成立那就是正確,沒有什麼有板無模這種事。

typename 的存廢與否不適合用 template class 證明,因 template class 自有自身檢驗機制,typename 對 template class 不具意義
作者 : sunyear(coco) VC++卓越專家C++頂尖高手貼文超過2000則
[ 貼文 2421 | 人氣 1485 | 評價 6060 | 評價/貼文 2.5 | 送出評價 5 次 ] 
[ 給個讚 ]  [ 給個讚 ]  [ 回應本文 ]  [ 發表新文 ]  [ 回上頁 ] [ 回討論區列表 ] [ 回知識入口 ]
2011/5/26 下午 12:43:48
>template class 的功能或者說目的本來就是把 template 參數套用上去,
>能讓整個 template class 處處皆成立那就是正確,沒有什麼有板無模這種事。
不是,所謂模板,就是框架。
舉個例子,你可以做一個電風扇的框架。
套上紅色參數,它就就是紅色的電風扇;
套上藍色參數,它就就是藍色的電風扇;
你不態做一個模板,既是電風扇的框架,也可以是照像機的框架。
這叫岐異性,是程式語言極力要避免和解決的。

>typename 的存廢與否不適合用 template class 證明,因 template class
>自有自身檢驗機制,typename 對 template class 不具意義
typename (目前為止)就是用在template,什麼叫不具意義?
template class自身檢驗機制,就是相依型態名稱要加typename,否則不視為型態。
或者你是在說編譯器各有自身檢驗機制(不依C++標準),那真的也沒什麼好說了。
作者 : cxxlman(CxxlMan) C++優秀好手貼文超過1000則
[ 貼文 1042 | 人氣 3227 | 評價 1260 | 評價/貼文 1.21 | 送出評價 28 次 ] 
[ 給個讚 ]  [ 給個讚 ]  [ 回應本文 ]  [ 發表新文 ]  [ 回上頁 ] [ 回討論區列表 ] [ 回知識入口 ]
2011/5/26 下午 02:12:18
程式設計師就是藉由程式來表現他的意圖,當程式設計師寫了一個 template class 這就是他的意圖,也才是真正的規範,這個規範相當於無形介面,一個 template 參數套用後會產生兩個不同的介面那幾乎是不可能的任務。

因你用的例子只有一行程式碼又是封閉式沒有後續或和 template class 其他部份有交互作用,所以會有兩個不同架構出現,在真正的 template class 這是不可能的

template class 是以自身的規範作為檢驗機制,所以通過 typename 的東西不見得符合 template class 自身的檢驗機制,未通過 typename 的東西也不能符合符合 template class 自身的檢驗機制,所以 typename 對 template class 並不具意義
 板主 : simula
 > C++ - 討論區
 - 最近熱門問答精華集
 - 全部歷史問答精華集
 - C++ - 知識庫
  ■ 全站最新Post列表
  ■ 我的文章收藏
  ■ 我最愛的作者
  ■ 全站文章收藏排行榜
  ■ 全站最愛作者排行榜
  ■  月熱門主題
  ■  季熱門主題
  ■  熱門主題Top 20
  ■  本區Post排行榜
  ■  本區評價排行榜
  ■  全站專家名人榜
  ■  全站Post排行榜
  ■  全站評價排行榜
  ■  全站人氣排行榜
 請輸入關鍵字 
  開始搜尋
 
Top 10
評價排行
C++
1 Raymond 13050 
2 青衫 4760 
3 simula 4690 
4 coco 4030 
5 白老鼠(Gary) 3670 
6 ozzy 2540 
7 Ben 2250 
8 Anderson 1960 
9 windblown 1650 
10 Kenny 1560 
C++
  專家等級 評價  
  一代宗師 10000  
  曠世奇才 5000  
  頂尖高手 3000  
  卓越專家 1500  
  優秀好手 750  
Microsoft Internet Explorer 6.0. Screen 1024x768 pixel. High Color (16 bit).
2000-2019 程式設計俱樂部 http://www.programmer-club.com.tw/
0.578125