天天看點

C++模闆 靜态成員 定義(執行個體化)

提出問題:

如果有這樣一個模闆:

template <typename T> class Test{
public:
    static std::string info;
};           

對于以下若幹種定義方式,哪些是對的(通過編譯)?

template <> string Test<int>::info("123");
template <typename T> string Test<T>::info("123");
template <typename T> string Test<T>::info;
template <> string Test<int>::info; 
template <> string Test<int>::info();
template <typename T> string Test<T>::info();           

為了不影響大家分析判斷,我把答案顔色調成比較淺的顔色,下面即是答案:

  1. template <> string Test<int>::info("123");//ok
  2. template <typename T> string Test<T>::info("123");//ok
  3. template <typename T> string Test<T>::info;//ok
  4. template <> string Test<int>::info; //error
  5. template <> string Test<int>::info();//error
  6. template <typename T> string Test<T>::info();//error

問題解答:

首先,說明一下三個正确的答案。

第一種形式稱之為特化定義,其作用是為模闆某一特化提供其靜态成員的定義,在我們例子中,它僅僅為Test<int>類的靜态成員info提供定義。并且調用單參數構造函數初始化。

第二種形式類似于普通類的靜态成員的定義方式,其作用是隐式地在該編譯單元為模闆的所有特化提供其靜态成員的定義,在我們例子中,在首次使用Test<int>,Test<float>,Test<char>...會隐式提供靜态成員的定義,并且調用單參數構造函數初始化。

第三種形式和第二種形式一緻,唯一不同就是采用預設構造函數初始化。

其次,說明一下三個錯誤的答案。

第一種形式,很多人都會認為是對的,認為它采用預設構造函數初始化。但編譯器會對特化定義進行特殊處理,編譯認為它是一個聲明而非定義。至于為什麼如此,應該詢問一下制定标準的人。我認為可能實作這樣的文法可能比較困難并且這個文法也比較雞肋。

第二種形式,這不成了聲明一個函數啦。

第三種形式,同第二種。

更多内容:

兩種正确的定義方式還有哪些其他的差別呢?

//a.cpp
template <typename T> string Test<T>::info("4321");
可以使用Test<int>::info
//b.cpp
template <typename T> string Test<T>::info("1234");
也可以使用Test<int>::info
           

這兩個定義可以在不同的編譯單元共存,Test<int>::info的初始值是多少,這取決與靜态成員的初始化順序,是以這不是件好事。

//a.cpp
template <> string Test<int>::info("123");
//b.cpp
template <> string Test<int>::info("123");           

而特化定義,上述方式無法通過編譯。

//a.cpp
template <> string Test<int>::info("123");

//b.cpp
template <typename T> string Test<T>::info("123");

一旦使用Test<int>::info無法通編譯            

上述方式無法通過編譯。

一般為了避免無法編譯,應當盡量減少使用,如下方式的定義

template <typename T> string Test<T>::info;           

隻有在你首次需要使用時在實作檔案中給出如下特化定義即可,其他檔案隻要包含頭檔案就能使用。

template <> string Test<int>::info("123");           

應用案例:

/*
 * Copyright (C) 2007 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#ifndef ANDROID_UTILS_SINGLETON_H
#define ANDROID_UTILS_SINGLETON_H

#include <stdint.h>
#include <sys/types.h>
#include <utils/threads.h>
#include <cutils/compiler.h>

namespace android {
// ---------------------------------------------------------------------------

template <typename TYPE>
class ANDROID_API Singleton
{
public:
    static TYPE& getInstance() {
        Mutex::Autolock _l(sLock);
        TYPE* instance = sInstance;
        if (instance == 0) {
            instance = new TYPE();
            sInstance = instance;
        }
        return *instance;
    }

    static bool hasInstance() {
        Mutex::Autolock _l(sLock);
        return sInstance != 0;
    }
    
protected:
    ~Singleton() { };
    Singleton() { };

private:
    //禁止複制構造函數和指派運算符函數,禁止類外部和内部以及友元調用 declare private,not define
    Singleton(const Singleton&);
    Singleton& operator = (const Singleton&);
    static Mutex sLock;
    static TYPE* sInstance;
};

/*
 * use ANDROID_SINGLETON_STATIC_INSTANCE(TYPE) in your implementation file
 * (eg: <TYPE>.cpp) to create the static instance of Singleton<>'s attributes,
 * and avoid to have a copy of them in each compilation units Singleton<TYPE>
 * is used.
 * 
 * NOTE: we use a version of Mutex ctor that takes a parameter, because
 * for some unknown reason using the default ctor doesn't emit the variable! 特化定義必須使用有參數的構造函數,否則認為是聲明!
 */
//想要使用Singleton,需要在自定義類型的實作檔案中包含此宏,用以初始化類模版static變量,并顯示執行個體化類模版
#define ANDROID_SINGLETON_STATIC_INSTANCE(TYPE)                 \
    template<> Mutex Singleton< TYPE >::sLock(Mutex::PRIVATE);  \ 特化定義
    template<> TYPE* Singleton< TYPE >::sInstance(0);           \ 特化定義
    template class Singleton< TYPE >; \顯示執行個體化


// ---------------------------------------------------------------------------
}; // namespace android