简体   繁体   English

仅针对带有枚举非类型模板参数的C ++模板函数的专业化

[英]Specializations only for C++ template function with enum non-type template parameter

This question is related to this one except that rather than dealing with typename template parameters, I am trying to use an enum non-type template parameter. 这个问题关系到这一个除了不是处理类型名称的模板参数,我想使用一个枚举非类型模板参数。

Is it possible to have a templated (class member function) with only specializations, no general (working) definition in the case of non-type template parameter? 在非类型模板参数的情况下,是否可能只有专门化的模板化(类成员函数),而没有一般性的(有效的)定义?

  1. I was able to get one version working, by declaration in the class body and providing specializations only, but any misuse calling with a non-defined template parameter doesn't produce an error until linking. 通过在类主体中进行声明并仅提供专业化功能,我能够使一个版本正常工作,但是使用未定义的模板参数进行的任何误用调用在链接之前都不会产生错误。 What's worse is the missing symbol cryptically refers to the enum's integral value and not its name, so it would be confusing to other developers. 更糟糕的是,缺少的符号以隐式方式指的是枚举的整数值,而不是其名称,因此它会使其他开发人员感到困惑。

  2. I was able to get the BOOST_STATIC_ASSERT technique from the referenced question to work for typename template parameter only. 我从引用的问题中获得了BOOST_STATIC_ASSERT技术,仅适用于typename模板参数。

This code demonstrates the idea. 这段代码演示了这个想法。 I don't want the CAT -version call to compile: 我不希望CAT -version调用进行编译:

#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;
}

Background: I have a class member function that takes an enum "ArgType" and a name. 背景:我有一个类成员函数,带有一个枚举“ ArgType”和一个名称。

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

The definition has turned into an if..else..if..else list for the half-dozen or so allowed ArgType cases. 该定义已成为六个或大约允许的ArgType情况的if..else..if..else列表。 I also have to have final case that throws an exception for an not-allowed ArgType. 我还必须有最后一个案例,该案例会为不允许的ArgType引发异常。 I'm thinking it would be cleaner to move ArgType to a template parameter, and provide a specialization for each allowed ArgType. 我认为将ArgType移至模板参数并为每个允许的ArgType提供专门化会更干净。 Misuse would be caught at compile-time. 滥用会在编译时被发现。

With partial specialization of a structure inside the class: 在类内部对结构进行部分专业化处理:

#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;
}

With full specialization of a (friend) class: 在(朋友)课程的完全专业化下:

#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;
}

Utilizing C++11 or boost::enable_if: 使用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;
}

From Herb Sutter 从草药萨特

It's a lot less intuitive to specialize function templates. 专门化功能模板要直观得多。 For one thing, you can't partially specialize them -- pretty much just because the language says you can't.[2] 一方面,您不能对它们进行部分专业化—仅仅是因为该语言表明您无法做到。[2] For another thing, function template specializations don't overload. 另一方面,功能模板专业化不会过载。 This means that any specializations you write will not affect which template gets used, which runs counter to what most people would intuitively expect. 这意味着您编写的任何专业化都不会影响使用哪个模板,这与大多数人的直觉期望背道而驰。 After all, if you had written a nontemplate function with the identical signature instead of a function template specialization, the nontemplate function would always be selected because it's always considered to be a better match than a template. 毕竟,如果您编写的非模板函数具有相同的签名而不是函数模板专门化,则总是会选择非模板函数,因为它总是被认为比模板更好。

If you're writing a function template, prefer to write it as a single function template that should never be specialized or overloaded, and implement the function template entirely in terms of a class template. 如果要编写功能模板,则最好将其编写为永远不应该专门化或重载的单个功能模板,并完全根据类模板来实现功能模板。 This is the proverbial level of indirection that steers you well clear of the limitations and dark corners of function templates. 这是间接的众所周知的级别,它使您清楚地了解功能模板的局限性和黑暗角落。 This way, programmers using your template will be able to partially specialize and explicitly specialize the class template to their heart's content without affecting the expected operation of the function template. 这样,使用您的模板的程序员将能够根据自己的喜好对类模板进行部分专业化和显式专业化,而不会影响功能模板的预期操作。 This avoids both the limitation that function templates can't be partially specialized, and the sometimes surprising effect that function template specializations don't overload. 这既避免了功能模板不能部分专业化的局限性,又避免了功能模板专业化不会过载的有时令人惊讶的效果。 Problem solved. 问题解决了。

Your enum type sizeof is not 0, change that to 4 at least. 您的枚举类型sizeof不为0,至少将其更改为4。 Otherwise this will not work. 否则,这将不起作用。 A enum element size is not 0. 枚举元素大小不为0。

Without that everything runs 没有这些,一切都会运行

#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;
}

The main difference between the enum case and the referenced question using a typename parameter is that the default definition will be compiled for any use. 枚举大小写和使用typename参数引用的问题之间的主要区别在于,将为任何用途编译默认定义。 So, a working solution is as simple as modifying the BOOST_STATIC_ASSERT condition to check allowed enum values. 因此,一个可行的解决方案就像修改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;
}

Of course, the whole design smells bad and could likely be handled more elegantly with AllowedTypes being a struct / class with inherited specializations. 当然,整个设计很难闻,可以使用AllowedTypes作为具有继承专业性的struct / class来进行更优雅的处理。 But this gets to the question at hand. 但这引起了手头的问题。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM