読者です 読者をやめる 読者になる 読者になる

私のひらめき日記

もっと自由に、流暢にプログラミングする

テンプレート引数が複数の場合

 クラステンプレートは部分特殊化(partial specialization)できるが、関数テンプレートやメンバ関数テンプレートは部分特殊化できない。

テンプレート 多重定義 部分特殊化 明示的特殊化
クラステンプレート ×
関数テンプレート ×

 部分特殊化よりも多重定義のほうがカバーできる範囲は少ない。
 明示的特殊化と多重定義は被る(template<>プレフィクスをつけてもつけなくても動作が同じ)になる場合がある。

例)

#include <iostream>

template<typename T>
T func(T value)
{
    return value;
}

//関数の多重定義(部分特殊化ではないことに注意。)そもそも、関数テンプレートは部分特殊化できない。
template<typename T>
T func(T* value)
{
    return (*value) * 2;
}

//こっちだと、関数の多重定義
//int func(int value)
//{
//    return 1;
//}

//こっちだと明示的特殊化
template<>
int func(int value)
{
    return value << 2;
}

int main(void)
{
    std::cout << func(3) << std::endl;
    
    int n = 3;
    int* pn = &n;
    std::cout << func(pn) << std::endl;
    
    return 0;
}


 テンプレート引数が複数の場合は、「関数テンプレートやメンバ関数テンプレートは部分特殊化できない」ということに特に気を付ける必要がある。
 一方を明示的に特殊化(下の場合はint sizeの部分)しても、残りのテンプレート(typename Tの部分)が特殊化されていない場合は、部分特殊化であることには変わりがない。結構はまったのでメモ。

template<typename T, int size>
T make_rev(const T init)
{
    return init*size;
}

//関数テンプレートの部分特殊化はできない
//template<typename T>
//T make_rev<T, 0>(const T init)
//{
//    return init;
//}

 こんなことをしたかったら、クラステンプレート(struct)の部分特殊化をすればよい:

#include <iostream>
#include <vector>

template<typename T, int size>
struct Make_rev
{
    static T func(const T init)
    {
        return init*size;
    }
};

//クラステンプレートの部分特殊化は可能
template<typename T>
struct Make_rev<T, 0>
{
    static T func(const T init)
    {
        return init;
    }
};

int main(void)
{   
    std::cout << Make_rev<int, 3>::func(5) << std::endl;
    std::cout << Make_rev<int, 0>::func(5) << std::endl;
    
    return 0;
}

 メンバ関数テンプレートも基本的な考え方は同じ:

#include <iostream>
#include <vector>

template<typename T, int size>
struct Make_rev;

template<typename T>
class Test
{
private:
	T value;     
public:
	Test(T value) : value(value) {}
	~Test() = default;

	template<int size>
		T rev(const T init)
	{
		//sizeのみの部分特殊化を行いたい。
		return Make_rev<T, size>::func(init, value);
	}
};


template<typename T, int size>
struct Make_rev
{
    static T func(const T init, const T value)
    {
        return init*size * value;
    }
};

//クラステンプレートの部分特殊化は可能
template<typename T>
struct Make_rev<T, 0>
{
    static T func(const T init, const T value)
    {
        return init * value;
    }
};

int main(void)
{   
    Test<int> t(7);
    
    std::cout << t.rev<3>(5) << std::endl;
    std::cout << t.rev<0>(5) << std::endl;

    return 0;
}

Visitor pattern

Test Visitor pattern
C++のためのAPIデザイン*1 12章を参照にした。

#include <iostream>
#include <vector>
#include <string>
#include <memory>

class ShapeNode;
class TransformNode;

class INodeVisitor
{
public:
    virtual ~INodeVisitor() {}
    virtual void Visit(ShapeNode& node) = 0;
    virtual void Visit(TransformNode& node) = 0;
};

class BaseNode
{
public:
    explicit BaseNode(const std::string& name) : mName(name) {}
    virtual ~BaseNode() {}
    virtual void Accept(INodeVisitor& visitor) = 0;
private:
    std::string mName;
};

class ShapeNode : public BaseNode
{
private:
public:
    explicit ShapeNode(const std::string& name) : BaseNode(name) {}
    void Accept(INodeVisitor& visitor)
    {
        visitor.Visit(*this);
    }
    int GetPolygonCount() const {return 4;}
};

class TransformNode : public BaseNode
{
private:
public:
    explicit TransformNode(const std::string& name) : BaseNode(name) {}
    void Accept(INodeVisitor& visitor)
    {
        visitor.Visit(*this);
    }
};

class SceneGraph
{
private:
    std::vector<BaseNode*> tree;
public:
    SceneGraph() = default;
    ~SceneGraph() = default;
    SceneGraph(const SceneGraph&) = delete;
    const SceneGraph& operator=(const SceneGraph&) = delete;
    void addScene(BaseNode* baseNode)
    {
        tree.push_back(baseNode);
    }
    void Traverse(INodeVisitor& visitor)
    {
        for(const auto& x : tree)
        {
            //MyVisitorクラスに個数カウントメソッドをまとめて、明記した
            x->Accept(visitor);
        }
    }
};

class MyVisitor : public INodeVisitor
{
private:
    int mNumShapes;
    int mNumPolygons;
    int mNumTransforms;
public:
    MyVisitor() : mNumShapes(), mNumPolygons(), mNumTransforms() {}
    void Visit(ShapeNode& node)
    {
        mNumPolygons += node.GetPolygonCount();
        ++mNumShapes;
    }
    
    void Visit(TransformNode&)
    {
        ++mNumTransforms;
    }
    int getNumShapes() {return mNumShapes; }
    int getNumPolygons() {return mNumPolygons; }
    int getNumTransforms() {return mNumTransforms; }
};

void print(MyVisitor& v)
{
    std::cout << "Shapes : " << v.getNumShapes() << std::endl;
    std::cout << "Polygons : " << v.getNumPolygons() << std::endl;
    std::cout << "Transforms : " << v.getNumTransforms() << std::endl;
}

int main(void)
{
    SceneGraph s;
    std::unique_ptr<BaseNode> n1(new ShapeNode("test1"));
    std::unique_ptr<BaseNode> n2(new TransformNode("test2"));
    s.addScene(n1.get());
    s.addScene(n2.get());
    
    MyVisitor visitor;
    s.Traverse(visitor);
    print(visitor);
    return 0;
}