[英]Auto non-type template parameter: ambiguous partial specializations in Clang
[英]Specializations only for C++ template function with enum non-type template parameter
这个问题关系到这一个除了不是处理类型名称的模板参数,我想使用一个枚举非类型模板参数。
在非类型模板参数的情况下,是否可能只有专门化的模板化(类成员函数),而没有一般性的(有效的)定义?
通过在类主体中进行声明并仅提供专业化功能,我能够使一个版本正常工作,但是使用未定义的模板参数进行的任何误用调用在链接之前都不会产生错误。 更糟糕的是,缺少的符号以隐式方式指的是枚举的整数值,而不是其名称,因此它会使其他开发人员感到困惑。
我从引用的问题中获得了BOOST_STATIC_ASSERT
技术,仅适用于typename模板参数。
这段代码演示了这个想法。 我不希望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;
}
背景:我有一个类成员函数,带有一个枚举“ ArgType”和一个名称。
void declareKernelArgument( ArgType type, std::string name );
该定义已成为六个或大约允许的ArgType情况的if..else..if..else
列表。 我还必须有最后一个案例,该案例会为不允许的ArgType引发异常。 我认为将ArgType移至模板参数并为每个允许的ArgType提供专门化会更干净。 滥用会在编译时被发现。
在类内部对结构进行部分专业化处理:
#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;
}
专门化功能模板要直观得多。 一方面,您不能对它们进行部分专业化—仅仅是因为该语言表明您无法做到。[2] 另一方面,功能模板专业化不会过载。 这意味着您编写的任何专业化都不会影响使用哪个模板,这与大多数人的直觉期望背道而驰。 毕竟,如果您编写的非模板函数具有相同的签名而不是函数模板专门化,则总是会选择非模板函数,因为它总是被认为比模板更好。
如果要编写功能模板,则最好将其编写为永远不应该专门化或重载的单个功能模板,并完全根据类模板来实现功能模板。 这是间接的众所周知的级别,它使您清楚地了解功能模板的局限性和黑暗角落。 这样,使用您的模板的程序员将能够根据自己的喜好对类模板进行部分专业化和显式专业化,而不会影响功能模板的预期操作。 这既避免了功能模板不能部分专业化的局限性,又避免了功能模板专业化不会过载的有时令人惊讶的效果。 问题解决了。
您的枚举类型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;
}
枚举大小写和使用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
来进行更优雅的处理。 但这引起了手头的问题。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.