Search code examples
c++visual-studiococos2d-xsfinae

Cannot be explicitly specialized in Visual Studio 2017


I am making a game playable on mac and windows with cocos2d-x.

I first wrote the code in Xcode, which could be run on mac.

I got an error when I took it to Windows and tried to build in Visual Studio 2017.

NRZNotification.h

#include "cocos2d.h"

class NRZNotification : public cocos2d::Ref
{
protected:
    std::string _name;
    cocos2d::Ref* _sender;

    ...

    cocos2d::ValueMap _valueMap;
    cocos2d::Map<std::string, cocos2d::Ref*> _objectMap;
public:
    const std::string& getName(){return _name;}
    cocos2d::Ref* getSender(){return _sender;}

    NRZNotification();
    virtual ~NRZNotification();
    static NRZNotification* create(const std::string& name, Ref* sender);
    bool init(const std::string& name, Ref* sender);

    ...

    template <typename T,
    typename std::enable_if<!std::is_convertible<T, cocos2d::Ref*>::value,
    std::nullptr_t>::type = nullptr>
    inline T getValue(const std::string& key)
    {
        //CCLOG("%s", __PRETTY_FUNCTION__);
        return 0;
    }
    template <typename T,
    typename std::enable_if<std::is_convertible<T, cocos2d::Ref*>::value,
    std::nullptr_t>::type = nullptr>
    inline T getValue(const std::string& key)
    {
        //CCLOG("%s", __PRETTY_FUNCTION__);
        return dynamic_cast<T>(_objectMap.at(key));
    }
};
#include "NRZNotification_Private.h"

NRZNotification_Private.h

#include "NRZNotification.h"

...

#pragma mark - get value

template <>
inline int NRZNotification::getValue<int,nullptr>(const std::string& key)
{
    if (_valueMap.find(key) == _valueMap.end()) {
        return 0;
    } else {
        return _valueMap.at(key).asInt();
    }
}
template <>
inline float NRZNotification::getValue(const std::string& key)
{
    if (_valueMap.find(key) == _valueMap.end()) {
        return 0.0f;
    } else {
        return _valueMap.at(key).asFloat();
    }
}
template <>
inline double NRZNotification::getValue(const std::string& key)
{
    if (_valueMap.find(key) == _valueMap.end()) {
        return 0.0;
    } else {
        return _valueMap.at(key).asDouble();
    }
}

...

These codes ran successfully on mac, but in Visual Studio 2017, calling getValue() gave the error "cannot be explicitly specialized".

getValue() is a function template, and the implementation is divided according to whether the return value is a subclass of cocos2d::Ref.

Furthermore, specialization is done for int, float, string, etc.

How should I fix this code?

I am using cocos2d-x 3.17.1.

Thank you.


Solution

  • I took the liberty to create a MCVE from your code.

    #include <type_traits>
    
    struct A {
        template<typename T, typename std::enable_if<!std::is_convertible<T, A>::value, int>::type = 0>
        T getValue() {
            return 1;
        }
        template<typename T, typename std::enable_if<std::is_convertible<T, A>::value, int>::type = 0>
        T getValue() {
            return T();
        }
    };
    
    template<>
    inline int A::getValue<int, 0>() {
        return 3;
    }
    
    int main() {
        A a;
        return a.getValue<int>();
    }
    

    Indeed, MSVC 2019 fails to compile it

    <source>(15): error C2910: 'A::getValue': cannot be explicitly specialized
    

    Whereas GCC and clang compile it just fine. Live demo.

    Fortunately the solution is simple - just remove the explicit template parameters. They are superfluous anyway:

    template<>
    inline int A::getValue() {
        return 3;
    }
    

    So for your case remove <int,nullptr> from

    template <>
    inline int NRZNotification::getValue<int,nullptr>(const std::string& key)