仅限具有枚举非类型模板参数的C ++模板函数的专门化

时间:2014-03-28 19:08:29

标签: c++ enums template-specialization static-assert

这个问题与this one有关,除了不是处理typename模板参数,我试图使用枚举非类型模板参数。

是否可以使用仅具有特化的模板化(类成员函数),在非类型模板参数的情况下没有通用(工作)定义?

  1. 我能够通过在类体中声明并仅提供特化来使一个版本工作,但是任何使用未定义模板参数的误操作都不会产生错误,直到链接。更糟糕的是,丢失的符号隐含地指的是枚举的整数值,而不是它的名称,因此它会让其他开发人员感到困惑。

  2. 我能够从引用的问题中获取BOOST_STATIC_ASSERT技术,仅适用于typename模板参数。

  3. 此代码演示了这个想法。我不希望CAT - 版本调用编译:

    #include <iostream>
    #include <boost/static_assert.hpp>
    
    // CLASS HEADER FILE:
    struct foo_class
    {
        enum AllowedTypes { DOG, CAT };
    
        template <AllowedTypes type>
        void add_one_third( double bar ) const
        {
            BOOST_STATIC_ASSERT_MSG(sizeof(type)==0, "enum type not supported.");
        }
    };
    
    // CLASS SOURCE FILE
    template<>
    void foo_class::add_one_third<foo_class::DOG>( double bar ) const
    {
        std::cout << "DOG specialization: " << bar + 1./3. << std::endl;
    }
    
    
    // USER SOURCE FILE
    int main()
    {
        std::cout << "Template Specialization!\n\n";
    
        foo_class a;
        a.add_one_third<foo_class::DOG>(3.0); // should succeed
        // Compilation fails with or without the following line:
        a.add_one_third<foo_class::CAT>(3.0); // should fail at compile-time
    
        return 0;
    }
    

    背景 我有一个类成员函数,它采用枚举&#34; ArgType&#34;和一个名字。

    void declareKernelArgument( ArgType type, std::string name );
    

    该定义已变成{6}允许的ArgType案例的if..else..if..else列表。我还必须有最终案例,为不允许的ArgType抛出异常。我认为将ArgType移动到模板参数会更清晰,并为每个允许的ArgType提供特化。滥用将在编译时捕获。

3 个答案:

答案 0 :(得分:5)

对班级内部结构进行部分专业化:

#include <iostream>

class foo_class
{
    public:
    enum AllowedTypes { T_DOUBLE, T_INT };

    private:
    template <AllowedTypes type, typename T>
    struct AddOneThird;

    template <typename T>
    struct AddOneThird<T_DOUBLE, T> {
        static void apply(T bar) {
            std::cout << "T_DOUBLE specialization: " << bar + 1.0/3.0 << std::endl;
        }
    };

    public:
    template <AllowedTypes type>
    void add_one_third( double bar ) const {
        AddOneThird<type, double>::apply(bar);
    }
};

int main() {
    foo_class a;
    a.add_one_third<foo_class::T_DOUBLE>(3.0);
    // error: incomplete type ‘foo_class::AddOneThird<(foo_class::AllowedTypes)1u
    // a.add_one_third<foo_class::T_INT>(3.0); // should fail at compile-time

    return 0;
}

完全专业化(朋友)课程:

#include <iostream>

class foo_class
{
    public:
    enum AllowedTypes { T_DOUBLE, T_INT };

    // if needed
    // template<AllowedTypes> friend struct AddOneThird;

    public:
    template <AllowedTypes type> void add_one_third( double bar ) const;
};

template <foo_class::AllowedTypes>
struct AddOneThird;

template <>
struct AddOneThird<foo_class::T_DOUBLE> {
    static void apply(double bar) {
        std::cout << "T_DOUBLE specialization: " << bar + 1.0/3.0 << std::endl;
    }
};

template <foo_class::AllowedTypes type>
void foo_class::add_one_third( double bar) const {
    AddOneThird<type>::apply(bar);
}


int main() {
    foo_class a;
    a.add_one_third<foo_class::T_DOUBLE>(3.0);
    // error: incomplete type ‘AddOneThird<(foo_class::AllowedTypes)1u>’ used
    //        in nested name specifier
    //a.add_one_third<foo_class::T_INT>(3.0); // should fail at compile-time

    return 0;
}

使用C ++ 11或boost :: enable_if:

#include <iostream>
#include <type_traits>

class foo_class
{
    public:
    enum AllowedTypes { T_DOUBLE, T_INT };

    template <AllowedTypes type>
    typename std::enable_if<type == T_DOUBLE>::type
    add_one_third( double bar ) const {
        std::cout << "T_DOUBLE specialization: " << bar + 1.0/3.0 << std::endl;
    }
};

int main() {
    foo_class a;
    a.add_one_third<foo_class::T_DOUBLE>(3.0);
    // error: no matching function for call to ‘foo_class::add_one_third(double)’
    //a.add_one_third<foo_class::T_INT>(3.0); // should fail at compile-time
    return 0;
}

答案 1 :(得分:1)

From Herb Sutter

  

专门化功能模板的直观性要低得多。首先,你不能对它们进行部分专业化 - 几乎只是因为语言说你做不到。[2]另一方面,函数模板特化不会重载。这意味着您编写的任何特化都不会影响使用哪个模板,这与大多数人直观期望的相反。毕竟,如果你用相同的签名而不是函数模板专门化编写了一个nontemplate函数,那么总会选择nontemplate函数,因为它总是被认为是比模板更好的匹配。

     

如果您正在编写函数模板,则更喜欢将其编写为永远不应该专门化或重载的单个函数模板,并完全根据类模板实现函数模板。这是众所周知的间接层面,可以让您远离功能模板的限制和黑暗角落。这样,使用您的模板的程序员将能够部分地专门化并明确地将类模板专门化为其内容的内容,而不会影响函数模板的预期操作。这避免了功能模板不能部分专用的限制,以及功能模板特化不会过载的有时令人惊讶的效果。问题解决了。

您的枚举类型sizeof不为0,至少将其更改为4。否则这将无效。枚举元素大小不是0.

如果没有,一切都会运行

#include <iostream>

struct foo_class
{
    enum AllowedTypes { DOG, CAT };

    template <AllowedTypes type>
    void add_one_third( double bar ) const
    {
        std::cout << "YES" << std::endl;
    }
};

template<>
void foo_class::add_one_third<foo_class::DOG>( double bar ) const
{
    std::cout << "DOG specialization: " << bar + 1./3. << std::endl;
}

int main()
{
    std::cout << "Template Specialization!\n\n";

    foo_class a;
    a.add_one_third<foo_class::DOG>(3.0); // should succeed
    // Compilation fails with or without the following line:
    //a.add_one_third<foo_class::CAT>(3.0); // should fail at compile-time

    return 0;
}

答案 2 :(得分:0)

使用typename参数的枚举案例和引用问题之间的主要区别在于,将编译默认定义以供任何使用。因此,工作解决方案就像修改BOOST_STATIC_ASSERT条件以检查允许的枚举值一样简单。

#include <iostream>
#include <stdexcept>
#include <boost/static_assert.hpp>

// CLASS HEADER FILE:
struct foo_class
{
    enum AllowedTypes { DOG, CAT, MOUSE };

    template <AllowedTypes type>
    void give_bath() const
    {
        // compile fails if ever attempting to use this function with CAT parameter.
        BOOST_STATIC_ASSERT_MSG( (type==DOG) || (type==MOUSE) , "enum type not supported.");
        throw std::runtime_error("Unexpected. Above list inconsistent with specializations.");   
    }
};

// CLASS SOURCE FILE
template<>
void foo_class::give_bath<foo_class::DOG>() const
{
    std::cout << "DOG is bathed." << std::endl;
}

template<>
void foo_class::give_bath<foo_class::MOUSE>() const
{
    std::cout << "MOUSE is bathed." << std::endl;
}


// USER SOURCE FILE
int main()
{
    std::cout << "Template Specialization!\n\n";

    foo_class a;
    a.give_bath<foo_class::DOG>(); //success
    a.give_bath<foo_class::MOUSE>(); // success
    // Compilation fails with the following line:
    //a.give_bath<foo_class::CAT>(); // fails at compile-time as intended.

    return 0;
}

当然,整个设计闻起来很糟糕,并且可能会更优雅地处理,其中AllowedTypes是具有继承特化的struct / class。但这就解决了手头的问题。