使用std :: enable_if有什么问题?

时间:2014-11-05 09:19:44

标签: c++ templates c++11

我有一个函数set_data,我必须以不同的方式实现它所采用的不同类型。例如,这是尝试根据输入类型实现两个重载。如果它是基础,非void和非nullptr_t,则在第一次实现时处理它。如果它是std::stringchar缓冲区,请以第二种方式处理它。

struct field
{
    template<typename TDataType, typename=void>
    void set_data(TDataType data, size_t index = 0);
};

template<typename TDataType, typename = typename
        std::enable_if< std::is_fundamental<TDataType>::value &&
                        std::is_same<TDataType, nullptr_t>::value == false &&
                        std::is_same<TDataType, void>::value == false>::type>
void field::set_data(TDataType data, size_t index /* = 0 */)
{
}

template<typename TDataType, typename = typename
        std::enable_if< std::is_same<std::string const &, TDataType> ||
                        std::is_same<char const *, TDataType>>::type>
void field::set_data(TDataType data, size_t index /* = 0 */)
{
}

然后我打电话给:

field f;
int x = 10;
f.set_data(x);

编译器向我抛出错误。

defs.h(111): error C2995: 'void messaging::field::set_data(TDataType,size_t)' : function template has already been defined

如何解决此问题?

在Visual Studio 2013上

3 个答案:

答案 0 :(得分:4)

您正在尝试定义类之外的重载,这当然是不允许的。或者您可能尝试为成员函数模板提供两个部分特化,但函数模板不能部分专门化。

你需要摆脱公共声明并将两个重载移到类中。但是,你必须使它们与众不同。您可以通过给其中一个额外的模板参数(并向第二个重载添加一些缺少的::value)来做到这一点:

struct field
{
    template<typename TDataType, typename = typename
            std::enable_if< std::is_fundamental<TDataType>::value &&
                            std::is_same<TDataType, std::nullptr_t>::value == false &&
                            std::is_same<TDataType, void>::value == false>::type>
    void set_data(TDataType data, size_t index = 0)
    {
    }

    template<typename TDataType, typename = void, typename = typename
            std::enable_if< std::is_same<std::string const &, TDataType>::value ||
                            std::is_same<char const *, TDataType>::value>::type>
    void set_data(TDataType data, size_t index = 0)
    {
    }
};

Live example

此外,请注意,如果函数模板的参数是按值传递,则模板参数永远不会推导为引用类型。因此,如果在调用站点上明确指定了模板参数is_same<std::string const &, TDataType>,则std::string const &只能为真。您可能只想检查std::string来替换它。甚至可能值得投入一些std::remove_referencestd::remove_cv来正确处理显式指定的模板参数。

答案 1 :(得分:3)

SFINAE {(1}}对(成员)函数(如示例中)的通常用法是返回类型(而不是其他模板参数)。这会给你

enable_if

答案 2 :(得分:2)

在这样的模板参数上使用SFINAE并不起作用,我认为我不能100%确定原因(编辑: Agnew的答案解释了它&#39 ; s因为模板是无法区分的)。如果您在返回类型上使用std::enable_if,并在类中移动定义,那么它可以工作(使用gcc和clang测试):

struct field
{
    template<typename TDataType>
    void set_data(TDataType data, size_t index = 0) {}

    template<typename TDataType>
    typename std::enable_if<std::is_fundamental<TDataType>::value &&
                            !std::is_same<TDataType, std::nullptr_t>::value &&
                            !std::is_same<TDataType, void>::value,
                            void>::type
    set_data(TDataType data, size_t index /* = 0 */)
    {
    }

    template<typename TDataType>
    typename std::enable_if<std::is_same<std::string const &, TDataType>::value ||
                            std::is_same<char const *, TDataType>::value,
                            void>::type
    set_data(TDataType data, size_t index /* = 0 */)
    {
    }

};

(我还修复了其他一些错误,即没有要求第二次重载的value std::is_same成员,而没有指定{{1}的命名空间})。