18480


自作クラスでテンプレート関数を作り、  
引数valueがRef*の場合とそうでない場合で処理を変えたいと思っています。

valueの型がint,float,double,std::string,boolなどの場合は_valueMapに格納し、  
Ref*の場合は_objectMapに格納するようにしたいです。

[code]

    class MyClass : public cocos2d::Ref
    {
    private:
        cocos2d::ValueMap _valueMap;
        cocos2d::Map<std::string, cocos2d::Ref*> _objectMap;
    public:
        template <typename T>
        inline void addValue(T value, const std::string& key);
    
        template <typename T>
        inline T getValue(const std::string& key);
    }
    
    template <typename T>
    inline void MyClass::addValue(T value, const std::string& key)
    {
        _valueMap[key] = value;
    }
    tempalte <>
    inline void MyClass::addValue(cocos2d::Ref* value, const std::string& key)
    {
        if (value) {
            _objectMap.insert(key, value);
        }
    }
    
[/code]

ところがこのコードだと、RefのサブクラスであるNodeや、その他の自作クラスのポインタをvalueとして渡したときに、特殊化した関数が呼ばれず、_valueMapのほうに格納されてしまいます。

関数を呼ぶときにaddValue＜Ref*＞とすれば大丈夫なのですが、書き忘れが怖いです。

Ref*の場合とそれ以外で関数を分けるしかないのでしょうか。

編集：  
回答ありがとうございます。  
Ref*の他に、TがVec2の場合の特殊化をするにはどうしたらいいのでしょうか。

[code]

    template <>
    inline void addValue<cocos2d::Vec2>(const cocos2d::Vec2& value, const std::string& key)
    {
        _vec2Map[key] = value;
    }
    
[/code]

と書いたらNo function template matches function template
specializationというエラーになりました。  
enable_ifの条件を追加して、Vec2の場合にも非テンプレート関数を書くしかないのでしょうか。

編集：  
解決しました。

MyClass.h

[code]

    class MyClass : public cocos2d::Ref
    {   
        std::unordered_map<std::string, cocos2d::Vec2> _vec2Map;
        std::unordered_map<std::string, cocos2d::Size> _sizeMap;
        std::unordered_map<std::string, cocos2d::Rect> _rectMap;
        cocos2d::ValueMap _valueMap;
        cocos2d::Map<std::string, cocos2d::Ref*> _objectMap;
    public:
        template <typename T,
        typename std::enable_if<!std::is_convertible<T, cocos2d::Ref*>::value,
        std::nullptr_t>::type = nullptr>
        inline void addValue(T value, const std::string& key)
        {
            CCLOG("%s", __PRETTY_FUNCTION__);
            _valueMap[key] = value;
        }
    
        void addValue(cocos2d::Ref* value, const std::string& key)
        {
            CCLOG("%s", __PRETTY_FUNCTION__);
            if (value) {
                _objectMap.insert(key, value);
            }
        }
    };
    #include "MyClass_Private.h"
    
[/code]

MyClass_Private.h

[code]

    #include "MyClass.h"
    
    template <>
    inline void MyClass::addValue<cocos2d::Vec2,nullptr>
    (cocos2d::Vec2 value, const std::string& key)
    {
        _vec2Map[key] = value;
    }
    template <>
    inline void MyClass::addValue<cocos2d::Size,nullptr>
    (cocos2d::Size value, const std::string& key)
    {
        _sizeMap[key] = value;
    }
    template <>
    inline void MyClass::addValue<cocos2d::Rect,nullptr>
    (cocos2d::Rect value, const std::string& key)
    {
        _rectMap[key] = value;
    }
    
[/code]


CC BY-SA 3.0
2015-11-06T11:06:55.153
18476
2015-11-07T12:23:23.320
2015-11-07T11:11:26.047
8825
8825
question
c++ cocos2d-x
引数がRef*の場合とそうでない場合で処理をわけるテンプレート関数


C++にはSFINAEという仕組みがあります。テンプレートの置き換えに失敗してもエラーにならないようにするとかなんとか。  
またそれをうまく利用するために`std::enable_if`他のテンプレートが用意されています。  
これらを使うと「TがRefやサブクラスの場合にはaddValue()テンプレート関数が定義されていなかったこと」にできます。その上で改めてaddValue(Ref*,)を非テンプレート関数として用意してやればいいでしょう。

[code]

    template<class T, std::enable_if_t<!std::is_base_of<Ref, std::remove_pointer_t<T>>::value>* = nullptr>
    void addValue(T value, const std::string& key) {
        //
    }
    void addValue(Ref* value, const std::string& key) {
        //
    }
    
[/code]

古いコンパイラーだと次のように記述する必要があるかもしれません。

[code]

    template<class T>
    typename std::enable_if<!std::is_base_of<Ref, typename std::remove_pointer<T>::type>::value>::type
    addValue(T value, const std::string& key) {
        //
    }
    
[/code]

* * *

元質問は

> valueの型がint,float,double,std::string,boolなどの場合は_valueMapに格納し、  
>  Ref*の場合は_objectMapに格納するようにしたいです。

でしたが追加質問では逆転していて、`Ref*`、`Vec2`、`Size`、`Rect`の場合は`_objectMap`で、残りが`_valueMap`と受け取れます。

であれば`enable_if`の型パラメーターはbool式ですので希望する式を記述すれば実現できます。

[code]

    // Refかその派生型へのポインター、Vec2、Size、Rectのいずれか
    template<class T, std::enable_if_t<
        std::is_pointer<T>::value && std::is_base_of<Ref, std::remove_pointer_t<T>>::value
        || std::is_same<Vec2, T>::value
        || std::is_same<Size, T>::value
        || std::is_same<Rect, T>::value
    >* = nullptr>
    void addValue(T value, const std::string& key) {
        //
    }
    // それ以外
    template<class T, std::enable_if_t<
        !(std::is_pointer<T>::value && std::is_base_of<Ref, std::remove_pointer_t<T>>::value
        || std::is_same<Vec2, T>::value
        || std::is_same<Size, T>::value
        || std::is_same<Rect, T>::value)
    >* = nullptr>
    void addValue(T value, const std::string& key) {
        //
    }
    
[/code]


CC BY-SA 3.0
2015-11-06T11:49:31.023
18480
2015-11-07T12:23:23.320
2015-11-07T12:23:23.320
4236
4236
18476
answer