如何防止 C++ 模板的特化?
How to prevent specialization of a C++ template?
当我这样做时编译器没有报错 ;-)
// Myfile.h
#include <iostream>
#include <vector>
namespace std
{
template<> class vector<int>
{
public:
vector ()
{
std::cout << "Happy Halloween !!!\n";
}
};
}
有什么方法可以防止 class/function 模板的这种不受欢迎的专业化?
--编辑--
我只是以 std:: 为例。我正在寻找的是一种防止这种情况发生在任何模板上的方法 class.
您所做的是在标准命名空间内专门化标准库类型。
除了一些已记录的自定义点(std::swap,std::hash<>)或用户定义类型的特定约束特化(例如MySmartPtr<T>
) 这违反了规范,结果是未定义的行为。
编辑: 没有针对这种违反规则的强制诊断。
为了让您图书馆的客户稍微更难把事情搞砸,您可以这样做:
namespace Public {
namespace Hidden { // DON'T TOUCH THESE!
template <typename> struct MyType { };
}
using Hidden::MyType;
}
现在,尝试在命名空间 Hidden
中特化 MyType<>
将会失败。
不,C++ 语言不提供您可以说 "don't allow specializations of this template" 的通用机制。
但这可能并不重要。对于您的代码已经使用的任何实例化,用户提供的专业化将违反单一定义规则,他们的程序可能会在火球中爆炸。
如果您没有使用库中的实例化,那么它们所做的并不重要。
这是在 C++ 中您根本无法阻止用户搬起石头砸自己的脚的情况之一,如果他们选择这样做,责任在他们身上。
有几种方法可以解决这个问题。您可以声明特定的特化而不定义它们。例如:
template<> struct MyStruct<int>;
那样的话,如果有人试图用一个 int 实例化,他们就会得到这个特化,它不会编译,因为没有定义。然后您可以编写一个简单的宏来为所有您不想专门化的类型执行此操作。
对于这个的反面,将定义设为空:
template<typename T> struct MyStruct{};
然后定义您计划支持的特定类型:
template<> struct MyStruct<int> {...};
template<> struct MyStruct<std::string> {...};
//etc...
编辑:
您可以在 C++11 中使用 enable_if 防止模板特化。虽然这是不切实际的。
#include <type_traits>
template<
typename real_t,
typename = typename std::enable_if<
std::is_floating_point<real_t>::value
>::type
>
struct check_t;
template<
typename real_t,
typename = typename
std::enable_if<
std::is_floating_point<real_t>::value,
check_t<real_t>
>::type
>
class vec_t
{
};
#if 1
template<>
class vec_t<int> {};
template<>
class vec_t<int,check_t<int>> {};
#endif
void test()
{
vec_t<float> vecf;
vec_t<int> veci;
}
main.cpp:26:16: error: no type named 'type' in 'struct std::enable_if<false, void>'
alias template 无法专门化,但具有 class 模板所需的行为。
template<class T>
struct my_class_implementation_which_should_not_be_used_directly
{
// impl
};
template<class T>
using my_class = my_class_implementation_which_should_not_be_used_directly<T>;
此外,您应该记录专门化 my_class_implementation_which_should_not_be_used_directly
会导致未定义的行为。现在,您的库用户无法意外特化 my_class
,并会直接收到关于 class 的警告。
防止此类行为的最佳方法是通过编码标准和代码审查。
在这种情况下,您不能强制编译器出错(除了使用其他答案中建议的解决方法之外),因为语言实际上允许这种行为,尽管它的真正用途可能会受到质疑。
问题是 - 您很可能 want/need 为多种类型提供通用行为(因此使用模板),但您需要为某些允许的类型提供特定的实现(特别是 std::string)
一个简单粗暴的例子(它是一个函数,但同样适用于 类)可能如下:
template<typename TData> TData GetData(std::string argument)
{
std::stringstream stream;
TData retVal;
stream.str(argument);
stream >> retVal;
return retVal;
}
然而,这将因 std::string 而失败,因为 >> 运算符将在第一个空白 space 后停止。所以你可以提供专门的专业化。
template<> std::string GetData(std::string argument)
{
return argument;
}
当我这样做时编译器没有报错 ;-)
// Myfile.h
#include <iostream>
#include <vector>
namespace std
{
template<> class vector<int>
{
public:
vector ()
{
std::cout << "Happy Halloween !!!\n";
}
};
}
有什么方法可以防止 class/function 模板的这种不受欢迎的专业化?
--编辑--
我只是以 std:: 为例。我正在寻找的是一种防止这种情况发生在任何模板上的方法 class.
您所做的是在标准命名空间内专门化标准库类型。
除了一些已记录的自定义点(std::swap,std::hash<>)或用户定义类型的特定约束特化(例如MySmartPtr<T>
) 这违反了规范,结果是未定义的行为。
编辑: 没有针对这种违反规则的强制诊断。
为了让您图书馆的客户稍微更难把事情搞砸,您可以这样做:
namespace Public {
namespace Hidden { // DON'T TOUCH THESE!
template <typename> struct MyType { };
}
using Hidden::MyType;
}
现在,尝试在命名空间 Hidden
中特化 MyType<>
将会失败。
不,C++ 语言不提供您可以说 "don't allow specializations of this template" 的通用机制。
但这可能并不重要。对于您的代码已经使用的任何实例化,用户提供的专业化将违反单一定义规则,他们的程序可能会在火球中爆炸。
如果您没有使用库中的实例化,那么它们所做的并不重要。
这是在 C++ 中您根本无法阻止用户搬起石头砸自己的脚的情况之一,如果他们选择这样做,责任在他们身上。
有几种方法可以解决这个问题。您可以声明特定的特化而不定义它们。例如:
template<> struct MyStruct<int>;
那样的话,如果有人试图用一个 int 实例化,他们就会得到这个特化,它不会编译,因为没有定义。然后您可以编写一个简单的宏来为所有您不想专门化的类型执行此操作。
对于这个的反面,将定义设为空:
template<typename T> struct MyStruct{};
然后定义您计划支持的特定类型:
template<> struct MyStruct<int> {...};
template<> struct MyStruct<std::string> {...};
//etc...
编辑:
您可以在 C++11 中使用 enable_if 防止模板特化。虽然这是不切实际的。
#include <type_traits>
template<
typename real_t,
typename = typename std::enable_if<
std::is_floating_point<real_t>::value
>::type
>
struct check_t;
template<
typename real_t,
typename = typename
std::enable_if<
std::is_floating_point<real_t>::value,
check_t<real_t>
>::type
>
class vec_t
{
};
#if 1
template<>
class vec_t<int> {};
template<>
class vec_t<int,check_t<int>> {};
#endif
void test()
{
vec_t<float> vecf;
vec_t<int> veci;
}
main.cpp:26:16: error: no type named 'type' in 'struct std::enable_if<false, void>'
alias template 无法专门化,但具有 class 模板所需的行为。
template<class T>
struct my_class_implementation_which_should_not_be_used_directly
{
// impl
};
template<class T>
using my_class = my_class_implementation_which_should_not_be_used_directly<T>;
此外,您应该记录专门化 my_class_implementation_which_should_not_be_used_directly
会导致未定义的行为。现在,您的库用户无法意外特化 my_class
,并会直接收到关于 class 的警告。
防止此类行为的最佳方法是通过编码标准和代码审查。
在这种情况下,您不能强制编译器出错(除了使用其他答案中建议的解决方法之外),因为语言实际上允许这种行为,尽管它的真正用途可能会受到质疑。 问题是 - 您很可能 want/need 为多种类型提供通用行为(因此使用模板),但您需要为某些允许的类型提供特定的实现(特别是 std::string)
一个简单粗暴的例子(它是一个函数,但同样适用于 类)可能如下:
template<typename TData> TData GetData(std::string argument)
{
std::stringstream stream;
TData retVal;
stream.str(argument);
stream >> retVal;
return retVal;
}
然而,这将因 std::string 而失败,因为 >> 运算符将在第一个空白 space 后停止。所以你可以提供专门的专业化。
template<> std::string GetData(std::string argument)
{
return argument;
}