テンプレートのインスタンス化 解答ページ | Programming Place Plus C++編【言語解説】 第21章

トップページC++編

C++編で扱っている C++ は 2003年に登場した C++03 という、とても古いバージョンのものです。C++ はその後、C++11 -> C++14 -> C++17 -> C++20 と更新されており、今後も 3年ごとに更新されます。
なかでも C++11 での更新は非常に大きなものであり、これから C++ の学習を始めるのなら、C++11 よりも古いバージョンを対象にするべきではありません。特に事情がないなら、新しい C++ を学んでください。 当サイトでは、C++14 をベースにした新C++編を作成中です。

問題①

問題① クラステンプレートのメンバ関数の定義が、ヘッダ側に書かれていないと、その関数を使用できないことを確認してください。その後、明示的なインスタンス化を行うことで、この問題を解決できることを確認してください。


// Stack.h

#ifndef STACK_H_INCLUDED
#define STACK_H_INCLUDED

#include <cstddef>

template <typename T>
class Stack {
public:
    typedef T value_type;

    explicit Stack(std::size_t capacity);
    ~Stack();

    void Push(const T& data);
    void Pop();
    inline const value_type Top() const
    {
        return mData[mSP - 1];
    }

    inline std::size_t GetSize() const
    {
        return mSP;
    }
    inline std::size_t GetCapacity() const
    {
        return mCapacity;
    }

private:
    const std::size_t      mCapacity;
    T*                     mData;
    int                    mSP;
};

#endif
// Stack.cpp

#include "Stack.h"
#include <cassert>

template <typename T>
Stack<T>::Stack(std::size_t capacity) :
    mCapacity(capacity),
    mData(new T[capacity]),
    mSP(0)
{
}

template <typename T>
Stack<T>::~Stack()
{
    delete [] mData;
}

template <typename T>
void Stack<T>::Push(const T& data)
{
    assert(static_cast<std::size_t>(mSP) < mCapacity);
    mData[mSP] = data;
    mSP++;
}

template <typename T>
void Stack<T>::Pop()
{
    assert(mSP > 0);
    mSP--;
}
// main.cpp

#include <iostream>
#include "stack.h"


int main()
{
    Stack<int> iStack(10);

    iStack.Push(5);
    std::cout << iStack.Top() << std::endl;
}

このプログラムを Visual Studio でビルドすると、次のようなエラーが出ます。

1>main.obj : error LNK2019: 未解決の外部シンボル "public: __thiscall Stack<int>::Stack<int>(unsigned int)" (??0?$Stack@H@@QAE@I@Z) が関数 _main で参照されました。
1>main.obj : error LNK2019: 未解決の外部シンボル "public: __thiscall Stack<int>::~Stack<int>(void)" (??1?$Stack@H@@QAE@XZ) が関数 _main で参照されました。
1>main.obj : error LNK2019: 未解決の外部シンボル "public: void __thiscall Stack<int>::Push(int const &)" (?Push@?$Stack@H@@QAEXABH@Z) が関数 _main で参照されました。
1>C:\test\Debug\CppTest2013.exe : fatal error LNK1120: 3 件の未解決の外部参照

これらはすべてリンク時のエラーで、それぞれ、コンストラクタ、デストラクタ、Pushメンバ関数の定義が見つからないことを報告しています。main関数内では、Topメンバ関数も使用していますが、これはインライン関数で、ヘッダ側に定義があるため、ちゃんと定義を見つけられていて、エラーになっていません。

今度は、stack.cpp の末尾に明示的なインスタンス化の指令を付け加えてみます。

// Stack.cpp

#include "Stack.h"
#include <cassert>

template <typename T>
Stack<T>::Stack(std::size_t capacity) :
    mCapacity(capacity),
    mData(new T[capacity]),
    mSP(0)
{
}

template <typename T>
Stack<T>::~Stack()
{
    delete [] mData;
}

template <typename T>
void Stack<T>::Push(const T& data)
{
    assert(static_cast<std::size_t>(mSP) < mCapacity);
    mData[mSP] = data;
    mSP++;
}

template <typename T>
void Stack<T>::Pop()
{
    assert(mSP > 0);
    mSP--;
}

// 明示的なインスタンス化
template class Stack<int>;

今度は問題なくコンパイル・リンクが通り、実行できます。テンプレート仮引数に int型を当てはめる形で、明示的なインスタンス化を行っています。この場合、stack.cpp をコンパイルした時点で、Stack<int> のインスタンスが生成されており、main.cpp からも、このインスタンスが使われます。


参考リンク


更新履歴

’2019/2/12 VisualStudio 2015 の対応終了。

’2018/4/2 「VisualC++」という表現を「VisualStudio」に統一。

’2015/8/15 VisualC++ 2015 に対応。

’2014/11/1 新規作成。



第21章のメインページへ

C++編のトップページへ

Programming Place Plus のトップページへ



はてなブックマーク に保存 Pocket に保存 Facebook でシェア
X で ポストフォロー LINE で送る noteで書く
rss1.0 取得ボタン RSS 管理者情報 プライバシーポリシー
先頭へ戻る