我可以从手动模板实例化中排除一些方法吗
Can I exclude some methods from manual template instantiation?
我们有一些复杂的模板类,它们有一些方法不能与某些策略或类型一起使用。因此,当我们检测到这些类型时(在编译时,使用类型特征),我们会发出一个带有漂亮消息的静态断言。
现在我们也进行了大量的手动模板实例化。部分原因是这些方法被强制编译来对这些方法进行语法检查。它还减少了库用户的编译时间。问题是静态断言总是被激发,因此我们无法手动实例化有问题的模板类。
有解决办法吗?
EDIT:为了更清楚,这里有一个例子(在这种情况下,显式实例化将在someFunc1()上失败:
// header
template <typename T>
class someClass
{
void someFunc() {}
void someFunc1() { static_assert(false, assertion_failed); }
};
// source
template someClass<int>; // Explicit instantiation
EDIT2:这里是另一个例子。这次你可以把它编译一下,看看我的意思。首先立即编译。代码应该编译。然后取消注释[2],静态断言应该被激发。现在注释出[2]和取消注释[1]。静态断言将被激发,因为您正在显式实例化模板。我希望避免删除显式实例化,因为它带来了好处(参见上面的好处)。
namespace Loki
{
template<int> struct CompileTimeError;
template<> struct CompileTimeError<true> {};
}
#define LOKI_STATIC_CHECK(expr, msg)
{ Loki::CompileTimeError<((expr) != 0)> ERROR_##msg; (void)ERROR_##msg; }
template <typename T>
class foo
{
public:
void func() {}
void func1() { LOKI_STATIC_CHECK(sizeof(T) == 4, Assertion_error); }
};
template foo<int>;
//template foo<double>; // [1]
int main()
{
foo<int> a;
a.func1();
foo<double> b;
//b.func1(); //[2]
return 0;
}
不能两者都有:不能有静态断言来阻止实例化和显式实例化类型!这是一个明显的矛盾。然而,你所能拥有的是有条件包含的功能,尽管这有点令人头疼:如果某个成员函数不应该支持某些类型,你可以将该函数移动到有条件包含它的基类中。这样你就不会使用静态断言,而只会删除该成员函数。我意识到这会引入其他有趣的问题,例如成员变量的位置,但我认为在你描述的上下文中,这是你能得到的最好的问题。
下面是一个快速的例子,说明这可能是什么样子:
template <typename T, bool = std::numeric_limits<T>::is_integer> struct foo_base;
template <typename T> struct foo_base<T, false> { /* intentionally left blank */ };
template <typename T> struct foo_base<T, true> { void foo() { /*...*/ } };
template <typename T>
struct Foo: foo_base<T> { /* .... */ };
template struct Foo<int>; // will have foo()
template struct Foo<double>; // will not have foo()
好吧,所以如果你使用显式实例化来强制实例化所有方法,那么你就无法逃脱任何编译时技巧来防止实例化有问题的方法,比如enable_if
。将错误转移到运行时是很容易的,但这是不可取的。
我认为你能做的最好的事情是将错误转移到链接时间,这将静态地确保程序不包含可能调用被禁止函数的代码路径,但错误消息对任何不知道你施加的限制的人都没有太大帮助。无论如何,解决方案是声明被禁止的成员函数的专门化,但不定义它们:
template<typename T>
struct Foo {
void bar() {
std::cout << "barn";
}
void baz() {
std:: cout << "bazn";
}
};
template<> void Foo<int>::baz(); // use of Foo<int>::baz() will resolve to this specialization, and linking will fail
template struct Foo<int>;
template struct Foo<char>;
int main() {
Foo<int> f;
f.bar();
// f.baz(); // uncommenting this line results in an ugly link time error
Foo<char> b;
b.bar();
b.baz(); // works with Foo<char>
}
当客户端代码中出现错误时,静态断言不再有助于提供漂亮的错误消息,但您可能希望保留它们,因为如果您忘记提供专门化,它们会被激发。
enable_if是一种灵活的机制,可以精确定位模板方法,这可能是您想要的。示例:
#include <string>
#include <iostream>
#include <boost/utility.hpp>
#include <boost/type_traits.hpp>
#include <boost/static_assert.hpp>
template <class T> class mywrapper
{
T _value;
template <class V>
typename boost::enable_if<boost::is_scalar<V>, void>::type printval_(V const& value)
{
BOOST_STATIC_ASSERT(boost::is_scalar<V>::value);
std::cout << "scalar: " << value << std::endl;
}
template <class V>
typename boost::enable_if<boost::is_compound<V>, void>::type printval_(V const& value)
{
BOOST_STATIC_ASSERT(boost::is_compound<V>::value);
std::cout << "compound: " << value << std::endl;
}
public:
mywrapper(T const& value):_value(value) { }
void printval() { printval_(_value); }
};
template class mywrapper<int>;
template class mywrapper<std::string>;
int main()
{
mywrapper<int> ival(333);
mywrapper<std::string> sval("test");
ival.printval();
sval.printval();
return 0;
}
我没有机会按照bobah的建议测试enable_if
,但我确实提出了一个不需要升压的解决方案,并且在很大程度上满足了我最初的要求(我说好而不是满,最后会解释)
解决方案是在代码上放置一个伪模板,如果在某些选定类型下编译,该模板将失败,而在其他类型下则可以。因此:
struct dummyStruct {};
#define DUMMY_TEMP typename dummy
#define DUMMY_PARAM dummyStruct
namespace Loki
{
template<int> struct CompileTimeError;
template<> struct CompileTimeError<true> {};
}
#define LOKI_STATIC_CHECK(expr, msg)
{ Loki::CompileTimeError<((expr) != 0)> ERROR_##msg; (void)ERROR_##msg; }
template <typename T>
class foo
{
public:
void func() {}
template <typename T_Dummy>
void func1() { LOKI_STATIC_CHECK(sizeof(T) == 4, Assertion_error); }
};
template foo<int>;
template foo<double>; // [1]
int main()
{
foo<int> a;
a.func1<DUMMY_PARAM>();
foo<double> b;
//b.func1<DUMMY_PARAM>(); //[2] - this is a static error
return 0;
}
在我的所有模板代码中,这些类型的函数(即那些对某些类型具有静态断言或有效的函数,并且可能通过使用类型特征(在这种情况下,可以为不同类型选择几个不同的函数)而对客户端隐藏。因此,在我的实现中,添加额外的dummy parameter
是一个不错的折衷方案。
额外的好处是,它让我知道这个功能是专为某些类型而设计的。此外,我最初的显式实例化问题就是通过这个简单的技术解决的。
- 为不同配置设置MSVC_RUNTIME_LIBRARY的正确方法是什么
- 通过方法访问结构
- 最小硬币更换问题(自上而下方法)
- C++为构建时间获取QDateTime的可靠方法
- 在C#中处理C++指针而不使用unsafe的最佳方法
- 处理多个异常集合的C++方法
- 如果C++类在类方法中具有动态分配,但没有构造函数/析构函数或任何非静态成员,那么它仍然是POD类型吗
- 有什么方法可以遍历结构吗
- 当类在C++中定义时,有什么方法可以"register"类吗?
- 在C++中,将大的无符号浮点数四舍五入为整数的最佳方法是什么
- 实现无开销push_back的最佳方法是什么
- 使用std::函数映射对象方法
- 有符号的int和int-有没有一种方法可以在C++中区分它们
- C++从另一个类访问公共静态向量的正确方法是什么
- C++优先级队列,按对象的唯一指针的特定方法升序排列
- 没有为自己的结构调用列表推回方法
- 有没有什么方法可以使用一个函数中定义的常量变量,也可以由c++中同一程序中的其他函数使用
- 如果未设置编译符号,如何排除方法?如何传递可变数量的参数并在函数体中格式化它们
- 我可以从手动模板实例化中排除一些方法吗
- 如何从最终二进制文件中排除未使用的方法